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';
3937 * @class Roo.bootstrap.NavHeaderbar
3938 * @extends Roo.bootstrap.NavSimplebar
3939 * Bootstrap Sidebar class
3941 * @cfg {String} brand what is brand
3942 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3943 * @cfg {String} brand_href href of the brand
3944 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3945 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3946 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3947 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3950 * Create a new Sidebar
3951 * @param {Object} config The config object
3955 Roo.bootstrap.NavHeaderbar = function(config){
3956 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3960 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3967 desktopCenter : false,
3970 getAutoCreate : function(){
3973 tag: this.nav || 'nav',
3980 if (this.desktopCenter) {
3981 cn.push({cls : 'container', cn : []});
3988 cls: 'navbar-header',
3993 cls: 'navbar-toggle',
3994 'data-toggle': 'collapse',
3999 html: 'Toggle navigation'
4021 cls: 'collapse navbar-collapse',
4025 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
4027 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4028 cfg.cls += ' navbar-' + this.position;
4030 // tag can override this..
4032 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4035 if (this.brand !== '') {
4038 href: this.brand_href ? this.brand_href : '#',
4039 cls: 'navbar-brand',
4047 cfg.cls += ' main-nav';
4055 getHeaderChildContainer : function()
4057 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4058 return this.el.select('.navbar-header',true).first();
4061 return this.getChildContainer();
4065 initEvents : function()
4067 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4069 if (this.autohide) {
4074 Roo.get(document).on('scroll',function(e) {
4075 var ns = Roo.get(document).getScroll().top;
4076 var os = prevScroll;
4080 ft.removeClass('slideDown');
4081 ft.addClass('slideUp');
4084 ft.removeClass('slideUp');
4085 ft.addClass('slideDown');
4106 * @class Roo.bootstrap.NavSidebar
4107 * @extends Roo.bootstrap.Navbar
4108 * Bootstrap Sidebar class
4111 * Create a new Sidebar
4112 * @param {Object} config The config object
4116 Roo.bootstrap.NavSidebar = function(config){
4117 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4120 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4122 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4124 getAutoCreate : function(){
4129 cls: 'sidebar sidebar-nav'
4151 * @class Roo.bootstrap.NavGroup
4152 * @extends Roo.bootstrap.Component
4153 * Bootstrap NavGroup class
4154 * @cfg {String} align (left|right)
4155 * @cfg {Boolean} inverse
4156 * @cfg {String} type (nav|pills|tab) default nav
4157 * @cfg {String} navId - reference Id for navbar.
4161 * Create a new nav group
4162 * @param {Object} config The config object
4165 Roo.bootstrap.NavGroup = function(config){
4166 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4169 Roo.bootstrap.NavGroup.register(this);
4173 * Fires when the active item changes
4174 * @param {Roo.bootstrap.NavGroup} this
4175 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4176 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4183 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4194 getAutoCreate : function()
4196 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4203 if (['tabs','pills'].indexOf(this.type)!==-1) {
4204 cfg.cls += ' nav-' + this.type
4206 if (this.type!=='nav') {
4207 Roo.log('nav type must be nav/tabs/pills')
4209 cfg.cls += ' navbar-nav'
4212 if (this.parent() && this.parent().sidebar) {
4215 cls: 'dashboard-menu sidebar-menu'
4221 if (this.form === true) {
4227 if (this.align === 'right') {
4228 cfg.cls += ' navbar-right';
4230 cfg.cls += ' navbar-left';
4234 if (this.align === 'right') {
4235 cfg.cls += ' navbar-right';
4239 cfg.cls += ' navbar-inverse';
4247 * sets the active Navigation item
4248 * @param {Roo.bootstrap.NavItem} the new current navitem
4250 setActiveItem : function(item)
4253 Roo.each(this.navItems, function(v){
4258 v.setActive(false, true);
4265 item.setActive(true, true);
4266 this.fireEvent('changed', this, item, prev);
4271 * gets the active Navigation item
4272 * @return {Roo.bootstrap.NavItem} the current navitem
4274 getActive : function()
4278 Roo.each(this.navItems, function(v){
4289 indexOfNav : function()
4293 Roo.each(this.navItems, function(v,i){
4304 * adds a Navigation item
4305 * @param {Roo.bootstrap.NavItem} the navitem to add
4307 addItem : function(cfg)
4309 var cn = new Roo.bootstrap.NavItem(cfg);
4311 cn.parentId = this.id;
4312 cn.onRender(this.el, null);
4316 * register a Navigation item
4317 * @param {Roo.bootstrap.NavItem} the navitem to add
4319 register : function(item)
4321 this.navItems.push( item);
4322 item.navId = this.navId;
4327 * clear all the Navigation item
4330 clearAll : function()
4333 this.el.dom.innerHTML = '';
4336 getNavItem: function(tabId)
4339 Roo.each(this.navItems, function(e) {
4340 if (e.tabId == tabId) {
4350 setActiveNext : function()
4352 var i = this.indexOfNav(this.getActive());
4353 if (i > this.navItems.length) {
4356 this.setActiveItem(this.navItems[i+1]);
4358 setActivePrev : function()
4360 var i = this.indexOfNav(this.getActive());
4364 this.setActiveItem(this.navItems[i-1]);
4366 clearWasActive : function(except) {
4367 Roo.each(this.navItems, function(e) {
4368 if (e.tabId != except.tabId && e.was_active) {
4369 e.was_active = false;
4376 getWasActive : function ()
4379 Roo.each(this.navItems, function(e) {
4394 Roo.apply(Roo.bootstrap.NavGroup, {
4398 * register a Navigation Group
4399 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4401 register : function(navgrp)
4403 this.groups[navgrp.navId] = navgrp;
4407 * fetch a Navigation Group based on the navigation ID
4408 * @param {string} the navgroup to add
4409 * @returns {Roo.bootstrap.NavGroup} the navgroup
4411 get: function(navId) {
4412 if (typeof(this.groups[navId]) == 'undefined') {
4414 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4416 return this.groups[navId] ;
4431 * @class Roo.bootstrap.NavItem
4432 * @extends Roo.bootstrap.Component
4433 * Bootstrap Navbar.NavItem class
4434 * @cfg {String} href link to
4435 * @cfg {String} html content of button
4436 * @cfg {String} badge text inside badge
4437 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4438 * @cfg {String} glyphicon name of glyphicon
4439 * @cfg {String} icon name of font awesome icon
4440 * @cfg {Boolean} active Is item active
4441 * @cfg {Boolean} disabled Is item disabled
4443 * @cfg {Boolean} preventDefault (true | false) default false
4444 * @cfg {String} tabId the tab that this item activates.
4445 * @cfg {String} tagtype (a|span) render as a href or span?
4446 * @cfg {Boolean} animateRef (true|false) link to element default false
4449 * Create a new Navbar Item
4450 * @param {Object} config The config object
4452 Roo.bootstrap.NavItem = function(config){
4453 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4458 * The raw click event for the entire grid.
4459 * @param {Roo.EventObject} e
4464 * Fires when the active item active state changes
4465 * @param {Roo.bootstrap.NavItem} this
4466 * @param {boolean} state the new state
4472 * Fires when scroll to element
4473 * @param {Roo.bootstrap.NavItem} this
4474 * @param {Object} options
4475 * @param {Roo.EventObject} e
4483 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4491 preventDefault : false,
4498 getAutoCreate : function(){
4507 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4509 if (this.disabled) {
4510 cfg.cls += ' disabled';
4513 if (this.href || this.html || this.glyphicon || this.icon) {
4517 href : this.href || "#",
4518 html: this.html || ''
4523 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4526 if(this.glyphicon) {
4527 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4532 cfg.cn[0].html += " <span class='caret'></span>";
4536 if (this.badge !== '') {
4538 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4546 initEvents: function()
4548 if (typeof (this.menu) != 'undefined') {
4549 this.menu.parentType = this.xtype;
4550 this.menu.triggerEl = this.el;
4551 this.menu = this.addxtype(Roo.apply({}, this.menu));
4554 this.el.select('a',true).on('click', this.onClick, this);
4556 if(this.tagtype == 'span'){
4557 this.el.select('span',true).on('click', this.onClick, this);
4560 // at this point parent should be available..
4561 this.parent().register(this);
4564 onClick : function(e)
4566 if (e.getTarget('.dropdown-menu-item')) {
4567 // did you click on a menu itemm.... - then don't trigger onclick..
4572 this.preventDefault ||
4575 Roo.log("NavItem - prevent Default?");
4579 if (this.disabled) {
4583 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4584 if (tg && tg.transition) {
4585 Roo.log("waiting for the transitionend");
4591 //Roo.log("fire event clicked");
4592 if(this.fireEvent('click', this, e) === false){
4596 if(this.tagtype == 'span'){
4600 //Roo.log(this.href);
4601 var ael = this.el.select('a',true).first();
4604 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4605 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4606 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4607 return; // ignore... - it's a 'hash' to another page.
4609 Roo.log("NavItem - prevent Default?");
4611 this.scrollToElement(e);
4615 var p = this.parent();
4617 if (['tabs','pills'].indexOf(p.type)!==-1) {
4618 if (typeof(p.setActiveItem) !== 'undefined') {
4619 p.setActiveItem(this);
4623 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4624 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4625 // remove the collapsed menu expand...
4626 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4630 isActive: function () {
4633 setActive : function(state, fire, is_was_active)
4635 if (this.active && !state && this.navId) {
4636 this.was_active = true;
4637 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4639 nv.clearWasActive(this);
4643 this.active = state;
4646 this.el.removeClass('active');
4647 } else if (!this.el.hasClass('active')) {
4648 this.el.addClass('active');
4651 this.fireEvent('changed', this, state);
4654 // show a panel if it's registered and related..
4656 if (!this.navId || !this.tabId || !state || is_was_active) {
4660 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4664 var pan = tg.getPanelByName(this.tabId);
4668 // if we can not flip to new panel - go back to old nav highlight..
4669 if (false == tg.showPanel(pan)) {
4670 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4672 var onav = nv.getWasActive();
4674 onav.setActive(true, false, true);
4683 // this should not be here...
4684 setDisabled : function(state)
4686 this.disabled = state;
4688 this.el.removeClass('disabled');
4689 } else if (!this.el.hasClass('disabled')) {
4690 this.el.addClass('disabled');
4696 * Fetch the element to display the tooltip on.
4697 * @return {Roo.Element} defaults to this.el
4699 tooltipEl : function()
4701 return this.el.select('' + this.tagtype + '', true).first();
4704 scrollToElement : function(e)
4706 var c = document.body;
4709 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4711 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4712 c = document.documentElement;
4715 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4721 var o = target.calcOffsetsTo(c);
4728 this.fireEvent('scrollto', this, options, e);
4730 Roo.get(c).scrollTo('top', options.value, true);
4743 * <span> icon </span>
4744 * <span> text </span>
4745 * <span>badge </span>
4749 * @class Roo.bootstrap.NavSidebarItem
4750 * @extends Roo.bootstrap.NavItem
4751 * Bootstrap Navbar.NavSidebarItem class
4752 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4753 * {Boolean} open is the menu open
4754 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4755 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4756 * {String} buttonSize (sm|md|lg)the extra classes for the button
4757 * {Boolean} showArrow show arrow next to the text (default true)
4759 * Create a new Navbar Button
4760 * @param {Object} config The config object
4762 Roo.bootstrap.NavSidebarItem = function(config){
4763 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4768 * The raw click event for the entire grid.
4769 * @param {Roo.EventObject} e
4774 * Fires when the active item active state changes
4775 * @param {Roo.bootstrap.NavSidebarItem} this
4776 * @param {boolean} state the new state
4784 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4786 badgeWeight : 'default',
4792 buttonWeight : 'default',
4798 getAutoCreate : function(){
4803 href : this.href || '#',
4809 if(this.buttonView){
4812 href : this.href || '#',
4813 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4826 cfg.cls += ' active';
4829 if (this.disabled) {
4830 cfg.cls += ' disabled';
4833 cfg.cls += ' open x-open';
4836 if (this.glyphicon || this.icon) {
4837 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4838 a.cn.push({ tag : 'i', cls : c }) ;
4841 if(!this.buttonView){
4844 html : this.html || ''
4851 if (this.badge !== '') {
4852 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4858 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4861 a.cls += ' dropdown-toggle treeview' ;
4867 initEvents : function()
4869 if (typeof (this.menu) != 'undefined') {
4870 this.menu.parentType = this.xtype;
4871 this.menu.triggerEl = this.el;
4872 this.menu = this.addxtype(Roo.apply({}, this.menu));
4875 this.el.on('click', this.onClick, this);
4877 if(this.badge !== ''){
4878 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4883 onClick : function(e)
4890 if(this.preventDefault){
4894 this.fireEvent('click', this);
4897 disable : function()
4899 this.setDisabled(true);
4904 this.setDisabled(false);
4907 setDisabled : function(state)
4909 if(this.disabled == state){
4913 this.disabled = state;
4916 this.el.addClass('disabled');
4920 this.el.removeClass('disabled');
4925 setActive : function(state)
4927 if(this.active == state){
4931 this.active = state;
4934 this.el.addClass('active');
4938 this.el.removeClass('active');
4943 isActive: function ()
4948 setBadge : function(str)
4954 this.badgeEl.dom.innerHTML = str;
4971 * @class Roo.bootstrap.Row
4972 * @extends Roo.bootstrap.Component
4973 * Bootstrap Row class (contains columns...)
4977 * @param {Object} config The config object
4980 Roo.bootstrap.Row = function(config){
4981 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4984 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4986 getAutoCreate : function(){
5005 * @class Roo.bootstrap.Element
5006 * @extends Roo.bootstrap.Component
5007 * Bootstrap Element class
5008 * @cfg {String} html contents of the element
5009 * @cfg {String} tag tag of the element
5010 * @cfg {String} cls class of the element
5011 * @cfg {Boolean} preventDefault (true|false) default false
5012 * @cfg {Boolean} clickable (true|false) default false
5015 * Create a new Element
5016 * @param {Object} config The config object
5019 Roo.bootstrap.Element = function(config){
5020 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5026 * When a element is chick
5027 * @param {Roo.bootstrap.Element} this
5028 * @param {Roo.EventObject} e
5034 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5039 preventDefault: false,
5042 getAutoCreate : function(){
5046 // cls: this.cls, double assign in parent class Component.js :: onRender
5053 initEvents: function()
5055 Roo.bootstrap.Element.superclass.initEvents.call(this);
5058 this.el.on('click', this.onClick, this);
5063 onClick : function(e)
5065 if(this.preventDefault){
5069 this.fireEvent('click', this, e);
5072 getValue : function()
5074 return this.el.dom.innerHTML;
5077 setValue : function(value)
5079 this.el.dom.innerHTML = value;
5094 * @class Roo.bootstrap.Pagination
5095 * @extends Roo.bootstrap.Component
5096 * Bootstrap Pagination class
5097 * @cfg {String} size xs | sm | md | lg
5098 * @cfg {Boolean} inverse false | true
5101 * Create a new Pagination
5102 * @param {Object} config The config object
5105 Roo.bootstrap.Pagination = function(config){
5106 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5109 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5115 getAutoCreate : function(){
5121 cfg.cls += ' inverse';
5127 cfg.cls += " " + this.cls;
5145 * @class Roo.bootstrap.PaginationItem
5146 * @extends Roo.bootstrap.Component
5147 * Bootstrap PaginationItem class
5148 * @cfg {String} html text
5149 * @cfg {String} href the link
5150 * @cfg {Boolean} preventDefault (true | false) default true
5151 * @cfg {Boolean} active (true | false) default false
5152 * @cfg {Boolean} disabled default false
5156 * Create a new PaginationItem
5157 * @param {Object} config The config object
5161 Roo.bootstrap.PaginationItem = function(config){
5162 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5167 * The raw click event for the entire grid.
5168 * @param {Roo.EventObject} e
5174 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5178 preventDefault: true,
5183 getAutoCreate : function(){
5189 href : this.href ? this.href : '#',
5190 html : this.html ? this.html : ''
5200 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5204 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5210 initEvents: function() {
5212 this.el.on('click', this.onClick, this);
5215 onClick : function(e)
5217 Roo.log('PaginationItem on click ');
5218 if(this.preventDefault){
5226 this.fireEvent('click', this, e);
5242 * @class Roo.bootstrap.Slider
5243 * @extends Roo.bootstrap.Component
5244 * Bootstrap Slider class
5247 * Create a new Slider
5248 * @param {Object} config The config object
5251 Roo.bootstrap.Slider = function(config){
5252 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5255 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5257 getAutoCreate : function(){
5261 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5265 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5277 * Ext JS Library 1.1.1
5278 * Copyright(c) 2006-2007, Ext JS, LLC.
5280 * Originally Released Under LGPL - original licence link has changed is not relivant.
5283 * <script type="text/javascript">
5288 * @class Roo.grid.ColumnModel
5289 * @extends Roo.util.Observable
5290 * This is the default implementation of a ColumnModel used by the Grid. It defines
5291 * the columns in the grid.
5294 var colModel = new Roo.grid.ColumnModel([
5295 {header: "Ticker", width: 60, sortable: true, locked: true},
5296 {header: "Company Name", width: 150, sortable: true},
5297 {header: "Market Cap.", width: 100, sortable: true},
5298 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5299 {header: "Employees", width: 100, sortable: true, resizable: false}
5304 * The config options listed for this class are options which may appear in each
5305 * individual column definition.
5306 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5308 * @param {Object} config An Array of column config objects. See this class's
5309 * config objects for details.
5311 Roo.grid.ColumnModel = function(config){
5313 * The config passed into the constructor
5315 this.config = config;
5318 // if no id, create one
5319 // if the column does not have a dataIndex mapping,
5320 // map it to the order it is in the config
5321 for(var i = 0, len = config.length; i < len; i++){
5323 if(typeof c.dataIndex == "undefined"){
5326 if(typeof c.renderer == "string"){
5327 c.renderer = Roo.util.Format[c.renderer];
5329 if(typeof c.id == "undefined"){
5332 if(c.editor && c.editor.xtype){
5333 c.editor = Roo.factory(c.editor, Roo.grid);
5335 if(c.editor && c.editor.isFormField){
5336 c.editor = new Roo.grid.GridEditor(c.editor);
5338 this.lookup[c.id] = c;
5342 * The width of columns which have no width specified (defaults to 100)
5345 this.defaultWidth = 100;
5348 * Default sortable of columns which have no sortable specified (defaults to false)
5351 this.defaultSortable = false;
5355 * @event widthchange
5356 * Fires when the width of a column changes.
5357 * @param {ColumnModel} this
5358 * @param {Number} columnIndex The column index
5359 * @param {Number} newWidth The new width
5361 "widthchange": true,
5363 * @event headerchange
5364 * Fires when the text of a header changes.
5365 * @param {ColumnModel} this
5366 * @param {Number} columnIndex The column index
5367 * @param {Number} newText The new header text
5369 "headerchange": true,
5371 * @event hiddenchange
5372 * Fires when a column is hidden or "unhidden".
5373 * @param {ColumnModel} this
5374 * @param {Number} columnIndex The column index
5375 * @param {Boolean} hidden true if hidden, false otherwise
5377 "hiddenchange": true,
5379 * @event columnmoved
5380 * Fires when a column is moved.
5381 * @param {ColumnModel} this
5382 * @param {Number} oldIndex
5383 * @param {Number} newIndex
5385 "columnmoved" : true,
5387 * @event columlockchange
5388 * Fires when a column's locked state is changed
5389 * @param {ColumnModel} this
5390 * @param {Number} colIndex
5391 * @param {Boolean} locked true if locked
5393 "columnlockchange" : true
5395 Roo.grid.ColumnModel.superclass.constructor.call(this);
5397 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5399 * @cfg {String} header The header text to display in the Grid view.
5402 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5403 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5404 * specified, the column's index is used as an index into the Record's data Array.
5407 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5408 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5411 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5412 * Defaults to the value of the {@link #defaultSortable} property.
5413 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5416 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5419 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5422 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5425 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5428 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5429 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5430 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5431 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5434 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5437 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5440 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5443 * @cfg {String} cursor (Optional)
5446 * @cfg {String} tooltip (Optional)
5449 * @cfg {Number} xs (Optional)
5452 * @cfg {Number} sm (Optional)
5455 * @cfg {Number} md (Optional)
5458 * @cfg {Number} lg (Optional)
5461 * Returns the id of the column at the specified index.
5462 * @param {Number} index The column index
5463 * @return {String} the id
5465 getColumnId : function(index){
5466 return this.config[index].id;
5470 * Returns the column for a specified id.
5471 * @param {String} id The column id
5472 * @return {Object} the column
5474 getColumnById : function(id){
5475 return this.lookup[id];
5480 * Returns the column for a specified dataIndex.
5481 * @param {String} dataIndex The column dataIndex
5482 * @return {Object|Boolean} the column or false if not found
5484 getColumnByDataIndex: function(dataIndex){
5485 var index = this.findColumnIndex(dataIndex);
5486 return index > -1 ? this.config[index] : false;
5490 * Returns the index for a specified column id.
5491 * @param {String} id The column id
5492 * @return {Number} the index, or -1 if not found
5494 getIndexById : function(id){
5495 for(var i = 0, len = this.config.length; i < len; i++){
5496 if(this.config[i].id == id){
5504 * Returns the index for a specified column dataIndex.
5505 * @param {String} dataIndex The column dataIndex
5506 * @return {Number} the index, or -1 if not found
5509 findColumnIndex : function(dataIndex){
5510 for(var i = 0, len = this.config.length; i < len; i++){
5511 if(this.config[i].dataIndex == dataIndex){
5519 moveColumn : function(oldIndex, newIndex){
5520 var c = this.config[oldIndex];
5521 this.config.splice(oldIndex, 1);
5522 this.config.splice(newIndex, 0, c);
5523 this.dataMap = null;
5524 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5527 isLocked : function(colIndex){
5528 return this.config[colIndex].locked === true;
5531 setLocked : function(colIndex, value, suppressEvent){
5532 if(this.isLocked(colIndex) == value){
5535 this.config[colIndex].locked = value;
5537 this.fireEvent("columnlockchange", this, colIndex, value);
5541 getTotalLockedWidth : function(){
5543 for(var i = 0; i < this.config.length; i++){
5544 if(this.isLocked(i) && !this.isHidden(i)){
5545 this.totalWidth += this.getColumnWidth(i);
5551 getLockedCount : function(){
5552 for(var i = 0, len = this.config.length; i < len; i++){
5553 if(!this.isLocked(i)){
5558 return this.config.length;
5562 * Returns the number of columns.
5565 getColumnCount : function(visibleOnly){
5566 if(visibleOnly === true){
5568 for(var i = 0, len = this.config.length; i < len; i++){
5569 if(!this.isHidden(i)){
5575 return this.config.length;
5579 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5580 * @param {Function} fn
5581 * @param {Object} scope (optional)
5582 * @return {Array} result
5584 getColumnsBy : function(fn, scope){
5586 for(var i = 0, len = this.config.length; i < len; i++){
5587 var c = this.config[i];
5588 if(fn.call(scope||this, c, i) === true){
5596 * Returns true if the specified column is sortable.
5597 * @param {Number} col The column index
5600 isSortable : function(col){
5601 if(typeof this.config[col].sortable == "undefined"){
5602 return this.defaultSortable;
5604 return this.config[col].sortable;
5608 * Returns the rendering (formatting) function defined for the column.
5609 * @param {Number} col The column index.
5610 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5612 getRenderer : function(col){
5613 if(!this.config[col].renderer){
5614 return Roo.grid.ColumnModel.defaultRenderer;
5616 return this.config[col].renderer;
5620 * Sets the rendering (formatting) function for a column.
5621 * @param {Number} col The column index
5622 * @param {Function} fn The function to use to process the cell's raw data
5623 * to return HTML markup for the grid view. The render function is called with
5624 * the following parameters:<ul>
5625 * <li>Data value.</li>
5626 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5627 * <li>css A CSS style string to apply to the table cell.</li>
5628 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5629 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5630 * <li>Row index</li>
5631 * <li>Column index</li>
5632 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5634 setRenderer : function(col, fn){
5635 this.config[col].renderer = fn;
5639 * Returns the width for the specified column.
5640 * @param {Number} col The column index
5643 getColumnWidth : function(col){
5644 return this.config[col].width * 1 || this.defaultWidth;
5648 * Sets the width for a column.
5649 * @param {Number} col The column index
5650 * @param {Number} width The new width
5652 setColumnWidth : function(col, width, suppressEvent){
5653 this.config[col].width = width;
5654 this.totalWidth = null;
5656 this.fireEvent("widthchange", this, col, width);
5661 * Returns the total width of all columns.
5662 * @param {Boolean} includeHidden True to include hidden column widths
5665 getTotalWidth : function(includeHidden){
5666 if(!this.totalWidth){
5667 this.totalWidth = 0;
5668 for(var i = 0, len = this.config.length; i < len; i++){
5669 if(includeHidden || !this.isHidden(i)){
5670 this.totalWidth += this.getColumnWidth(i);
5674 return this.totalWidth;
5678 * Returns the header for the specified column.
5679 * @param {Number} col The column index
5682 getColumnHeader : function(col){
5683 return this.config[col].header;
5687 * Sets the header for a column.
5688 * @param {Number} col The column index
5689 * @param {String} header The new header
5691 setColumnHeader : function(col, header){
5692 this.config[col].header = header;
5693 this.fireEvent("headerchange", this, col, header);
5697 * Returns the tooltip for the specified column.
5698 * @param {Number} col The column index
5701 getColumnTooltip : function(col){
5702 return this.config[col].tooltip;
5705 * Sets the tooltip for a column.
5706 * @param {Number} col The column index
5707 * @param {String} tooltip The new tooltip
5709 setColumnTooltip : function(col, tooltip){
5710 this.config[col].tooltip = tooltip;
5714 * Returns the dataIndex for the specified column.
5715 * @param {Number} col The column index
5718 getDataIndex : function(col){
5719 return this.config[col].dataIndex;
5723 * Sets the dataIndex for a column.
5724 * @param {Number} col The column index
5725 * @param {Number} dataIndex The new dataIndex
5727 setDataIndex : function(col, dataIndex){
5728 this.config[col].dataIndex = dataIndex;
5734 * Returns true if the cell is editable.
5735 * @param {Number} colIndex The column index
5736 * @param {Number} rowIndex The row index - this is nto actually used..?
5739 isCellEditable : function(colIndex, rowIndex){
5740 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5744 * Returns the editor defined for the cell/column.
5745 * return false or null to disable editing.
5746 * @param {Number} colIndex The column index
5747 * @param {Number} rowIndex The row index
5750 getCellEditor : function(colIndex, rowIndex){
5751 return this.config[colIndex].editor;
5755 * Sets if a column is editable.
5756 * @param {Number} col The column index
5757 * @param {Boolean} editable True if the column is editable
5759 setEditable : function(col, editable){
5760 this.config[col].editable = editable;
5765 * Returns true if the column is hidden.
5766 * @param {Number} colIndex The column index
5769 isHidden : function(colIndex){
5770 return this.config[colIndex].hidden;
5775 * Returns true if the column width cannot be changed
5777 isFixed : function(colIndex){
5778 return this.config[colIndex].fixed;
5782 * Returns true if the column can be resized
5785 isResizable : function(colIndex){
5786 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5789 * Sets if a column is hidden.
5790 * @param {Number} colIndex The column index
5791 * @param {Boolean} hidden True if the column is hidden
5793 setHidden : function(colIndex, hidden){
5794 this.config[colIndex].hidden = hidden;
5795 this.totalWidth = null;
5796 this.fireEvent("hiddenchange", this, colIndex, hidden);
5800 * Sets the editor for a column.
5801 * @param {Number} col The column index
5802 * @param {Object} editor The editor object
5804 setEditor : function(col, editor){
5805 this.config[col].editor = editor;
5809 Roo.grid.ColumnModel.defaultRenderer = function(value)
5811 if(typeof value == "object") {
5814 if(typeof value == "string" && value.length < 1){
5818 return String.format("{0}", value);
5821 // Alias for backwards compatibility
5822 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5825 * Ext JS Library 1.1.1
5826 * Copyright(c) 2006-2007, Ext JS, LLC.
5828 * Originally Released Under LGPL - original licence link has changed is not relivant.
5831 * <script type="text/javascript">
5835 * @class Roo.LoadMask
5836 * A simple utility class for generically masking elements while loading data. If the element being masked has
5837 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5838 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5839 * element's UpdateManager load indicator and will be destroyed after the initial load.
5841 * Create a new LoadMask
5842 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5843 * @param {Object} config The config object
5845 Roo.LoadMask = function(el, config){
5846 this.el = Roo.get(el);
5847 Roo.apply(this, config);
5849 this.store.on('beforeload', this.onBeforeLoad, this);
5850 this.store.on('load', this.onLoad, this);
5851 this.store.on('loadexception', this.onLoadException, this);
5852 this.removeMask = false;
5854 var um = this.el.getUpdateManager();
5855 um.showLoadIndicator = false; // disable the default indicator
5856 um.on('beforeupdate', this.onBeforeLoad, this);
5857 um.on('update', this.onLoad, this);
5858 um.on('failure', this.onLoad, this);
5859 this.removeMask = true;
5863 Roo.LoadMask.prototype = {
5865 * @cfg {Boolean} removeMask
5866 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5867 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5871 * The text to display in a centered loading message box (defaults to 'Loading...')
5875 * @cfg {String} msgCls
5876 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5878 msgCls : 'x-mask-loading',
5881 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5887 * Disables the mask to prevent it from being displayed
5889 disable : function(){
5890 this.disabled = true;
5894 * Enables the mask so that it can be displayed
5896 enable : function(){
5897 this.disabled = false;
5900 onLoadException : function()
5904 if (typeof(arguments[3]) != 'undefined') {
5905 Roo.MessageBox.alert("Error loading",arguments[3]);
5909 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5910 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5917 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5922 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5926 onBeforeLoad : function(){
5928 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5933 destroy : function(){
5935 this.store.un('beforeload', this.onBeforeLoad, this);
5936 this.store.un('load', this.onLoad, this);
5937 this.store.un('loadexception', this.onLoadException, this);
5939 var um = this.el.getUpdateManager();
5940 um.un('beforeupdate', this.onBeforeLoad, this);
5941 um.un('update', this.onLoad, this);
5942 um.un('failure', this.onLoad, this);
5953 * @class Roo.bootstrap.Table
5954 * @extends Roo.bootstrap.Component
5955 * Bootstrap Table class
5956 * @cfg {String} cls table class
5957 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5958 * @cfg {String} bgcolor Specifies the background color for a table
5959 * @cfg {Number} border Specifies whether the table cells should have borders or not
5960 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5961 * @cfg {Number} cellspacing Specifies the space between cells
5962 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5963 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5964 * @cfg {String} sortable Specifies that the table should be sortable
5965 * @cfg {String} summary Specifies a summary of the content of a table
5966 * @cfg {Number} width Specifies the width of a table
5967 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5969 * @cfg {boolean} striped Should the rows be alternative striped
5970 * @cfg {boolean} bordered Add borders to the table
5971 * @cfg {boolean} hover Add hover highlighting
5972 * @cfg {boolean} condensed Format condensed
5973 * @cfg {boolean} responsive Format condensed
5974 * @cfg {Boolean} loadMask (true|false) default false
5975 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5976 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5977 * @cfg {Boolean} rowSelection (true|false) default false
5978 * @cfg {Boolean} cellSelection (true|false) default false
5979 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5980 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5981 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5982 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
5986 * Create a new Table
5987 * @param {Object} config The config object
5990 Roo.bootstrap.Table = function(config){
5991 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5996 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5997 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5998 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5999 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6001 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6003 this.sm.grid = this;
6004 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6005 this.sm = this.selModel;
6006 this.sm.xmodule = this.xmodule || false;
6009 if (this.cm && typeof(this.cm.config) == 'undefined') {
6010 this.colModel = new Roo.grid.ColumnModel(this.cm);
6011 this.cm = this.colModel;
6012 this.cm.xmodule = this.xmodule || false;
6015 this.store= Roo.factory(this.store, Roo.data);
6016 this.ds = this.store;
6017 this.ds.xmodule = this.xmodule || false;
6020 if (this.footer && this.store) {
6021 this.footer.dataSource = this.ds;
6022 this.footer = Roo.factory(this.footer);
6029 * Fires when a cell is clicked
6030 * @param {Roo.bootstrap.Table} this
6031 * @param {Roo.Element} el
6032 * @param {Number} rowIndex
6033 * @param {Number} columnIndex
6034 * @param {Roo.EventObject} e
6038 * @event celldblclick
6039 * Fires when a cell is double clicked
6040 * @param {Roo.bootstrap.Table} this
6041 * @param {Roo.Element} el
6042 * @param {Number} rowIndex
6043 * @param {Number} columnIndex
6044 * @param {Roo.EventObject} e
6046 "celldblclick" : true,
6049 * Fires when a row is clicked
6050 * @param {Roo.bootstrap.Table} this
6051 * @param {Roo.Element} el
6052 * @param {Number} rowIndex
6053 * @param {Roo.EventObject} e
6057 * @event rowdblclick
6058 * Fires when a row is double clicked
6059 * @param {Roo.bootstrap.Table} this
6060 * @param {Roo.Element} el
6061 * @param {Number} rowIndex
6062 * @param {Roo.EventObject} e
6064 "rowdblclick" : true,
6067 * Fires when a mouseover occur
6068 * @param {Roo.bootstrap.Table} this
6069 * @param {Roo.Element} el
6070 * @param {Number} rowIndex
6071 * @param {Number} columnIndex
6072 * @param {Roo.EventObject} e
6077 * Fires when a mouseout occur
6078 * @param {Roo.bootstrap.Table} this
6079 * @param {Roo.Element} el
6080 * @param {Number} rowIndex
6081 * @param {Number} columnIndex
6082 * @param {Roo.EventObject} e
6087 * Fires when a row is rendered, so you can change add a style to it.
6088 * @param {Roo.bootstrap.Table} this
6089 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6093 * @event rowsrendered
6094 * Fires when all the rows have been rendered
6095 * @param {Roo.bootstrap.Table} this
6097 'rowsrendered' : true,
6099 * @event contextmenu
6100 * The raw contextmenu event for the entire grid.
6101 * @param {Roo.EventObject} e
6103 "contextmenu" : true,
6105 * @event rowcontextmenu
6106 * Fires when a row is right clicked
6107 * @param {Roo.bootstrap.Table} this
6108 * @param {Number} rowIndex
6109 * @param {Roo.EventObject} e
6111 "rowcontextmenu" : true,
6113 * @event cellcontextmenu
6114 * Fires when a cell is right clicked
6115 * @param {Roo.bootstrap.Table} this
6116 * @param {Number} rowIndex
6117 * @param {Number} cellIndex
6118 * @param {Roo.EventObject} e
6120 "cellcontextmenu" : true,
6122 * @event headercontextmenu
6123 * Fires when a header is right clicked
6124 * @param {Roo.bootstrap.Table} this
6125 * @param {Number} columnIndex
6126 * @param {Roo.EventObject} e
6128 "headercontextmenu" : true
6132 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6158 rowSelection : false,
6159 cellSelection : false,
6162 // Roo.Element - the tbody
6164 // Roo.Element - thead element
6167 container: false, // used by gridpanel...
6173 auto_hide_footer : false,
6175 getAutoCreate : function()
6177 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6184 if (this.scrollBody) {
6185 cfg.cls += ' table-body-fixed';
6188 cfg.cls += ' table-striped';
6192 cfg.cls += ' table-hover';
6194 if (this.bordered) {
6195 cfg.cls += ' table-bordered';
6197 if (this.condensed) {
6198 cfg.cls += ' table-condensed';
6200 if (this.responsive) {
6201 cfg.cls += ' table-responsive';
6205 cfg.cls+= ' ' +this.cls;
6208 // this lot should be simplifed...
6221 ].forEach(function(k) {
6229 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6232 if(this.store || this.cm){
6233 if(this.headerShow){
6234 cfg.cn.push(this.renderHeader());
6237 cfg.cn.push(this.renderBody());
6239 if(this.footerShow){
6240 cfg.cn.push(this.renderFooter());
6242 // where does this come from?
6243 //cfg.cls+= ' TableGrid';
6246 return { cn : [ cfg ] };
6249 initEvents : function()
6251 if(!this.store || !this.cm){
6254 if (this.selModel) {
6255 this.selModel.initEvents();
6259 //Roo.log('initEvents with ds!!!!');
6261 this.mainBody = this.el.select('tbody', true).first();
6262 this.mainHead = this.el.select('thead', true).first();
6263 this.mainFoot = this.el.select('tfoot', true).first();
6269 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6270 e.on('click', _this.sort, _this);
6273 this.mainBody.on("click", this.onClick, this);
6274 this.mainBody.on("dblclick", this.onDblClick, this);
6276 // why is this done????? = it breaks dialogs??
6277 //this.parent().el.setStyle('position', 'relative');
6281 this.footer.parentId = this.id;
6282 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6285 this.el.select('tfoot tr td').first().addClass('hide');
6290 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6293 this.store.on('load', this.onLoad, this);
6294 this.store.on('beforeload', this.onBeforeLoad, this);
6295 this.store.on('update', this.onUpdate, this);
6296 this.store.on('add', this.onAdd, this);
6297 this.store.on("clear", this.clear, this);
6299 this.el.on("contextmenu", this.onContextMenu, this);
6301 this.mainBody.on('scroll', this.onBodyScroll, this);
6303 this.cm.on("headerchange", this.onHeaderChange, this);
6305 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6309 onContextMenu : function(e, t)
6311 this.processEvent("contextmenu", e);
6314 processEvent : function(name, e)
6316 if (name != 'touchstart' ) {
6317 this.fireEvent(name, e);
6320 var t = e.getTarget();
6322 var cell = Roo.get(t);
6328 if(cell.findParent('tfoot', false, true)){
6332 if(cell.findParent('thead', false, true)){
6334 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6335 cell = Roo.get(t).findParent('th', false, true);
6337 Roo.log("failed to find th in thead?");
6338 Roo.log(e.getTarget());
6343 var cellIndex = cell.dom.cellIndex;
6345 var ename = name == 'touchstart' ? 'click' : name;
6346 this.fireEvent("header" + ename, this, cellIndex, e);
6351 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6352 cell = Roo.get(t).findParent('td', false, true);
6354 Roo.log("failed to find th in tbody?");
6355 Roo.log(e.getTarget());
6360 var row = cell.findParent('tr', false, true);
6361 var cellIndex = cell.dom.cellIndex;
6362 var rowIndex = row.dom.rowIndex - 1;
6366 this.fireEvent("row" + name, this, rowIndex, e);
6370 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6376 onMouseover : function(e, el)
6378 var cell = Roo.get(el);
6384 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6385 cell = cell.findParent('td', false, true);
6388 var row = cell.findParent('tr', false, true);
6389 var cellIndex = cell.dom.cellIndex;
6390 var rowIndex = row.dom.rowIndex - 1; // start from 0
6392 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6396 onMouseout : function(e, el)
6398 var cell = Roo.get(el);
6404 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6405 cell = cell.findParent('td', false, true);
6408 var row = cell.findParent('tr', false, true);
6409 var cellIndex = cell.dom.cellIndex;
6410 var rowIndex = row.dom.rowIndex - 1; // start from 0
6412 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6416 onClick : function(e, el)
6418 var cell = Roo.get(el);
6420 if(!cell || (!this.cellSelection && !this.rowSelection)){
6424 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6425 cell = cell.findParent('td', false, true);
6428 if(!cell || typeof(cell) == 'undefined'){
6432 var row = cell.findParent('tr', false, true);
6434 if(!row || typeof(row) == 'undefined'){
6438 var cellIndex = cell.dom.cellIndex;
6439 var rowIndex = this.getRowIndex(row);
6441 // why??? - should these not be based on SelectionModel?
6442 if(this.cellSelection){
6443 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6446 if(this.rowSelection){
6447 this.fireEvent('rowclick', this, row, rowIndex, e);
6453 onDblClick : function(e,el)
6455 var cell = Roo.get(el);
6457 if(!cell || (!this.cellSelection && !this.rowSelection)){
6461 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6462 cell = cell.findParent('td', false, true);
6465 if(!cell || typeof(cell) == 'undefined'){
6469 var row = cell.findParent('tr', false, true);
6471 if(!row || typeof(row) == 'undefined'){
6475 var cellIndex = cell.dom.cellIndex;
6476 var rowIndex = this.getRowIndex(row);
6478 if(this.cellSelection){
6479 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6482 if(this.rowSelection){
6483 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6487 sort : function(e,el)
6489 var col = Roo.get(el);
6491 if(!col.hasClass('sortable')){
6495 var sort = col.attr('sort');
6498 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6502 this.store.sortInfo = {field : sort, direction : dir};
6505 Roo.log("calling footer first");
6506 this.footer.onClick('first');
6509 this.store.load({ params : { start : 0 } });
6513 renderHeader : function()
6521 this.totalWidth = 0;
6523 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6525 var config = cm.config[i];
6529 cls : 'x-hcol-' + i,
6531 html: cm.getColumnHeader(i)
6536 if(typeof(config.sortable) != 'undefined' && config.sortable){
6538 c.html = '<i class="glyphicon"></i>' + c.html;
6541 if(typeof(config.lgHeader) != 'undefined'){
6542 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6545 if(typeof(config.mdHeader) != 'undefined'){
6546 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6549 if(typeof(config.smHeader) != 'undefined'){
6550 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6553 if(typeof(config.xsHeader) != 'undefined'){
6554 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6561 if(typeof(config.tooltip) != 'undefined'){
6562 c.tooltip = config.tooltip;
6565 if(typeof(config.colspan) != 'undefined'){
6566 c.colspan = config.colspan;
6569 if(typeof(config.hidden) != 'undefined' && config.hidden){
6570 c.style += ' display:none;';
6573 if(typeof(config.dataIndex) != 'undefined'){
6574 c.sort = config.dataIndex;
6579 if(typeof(config.align) != 'undefined' && config.align.length){
6580 c.style += ' text-align:' + config.align + ';';
6583 if(typeof(config.width) != 'undefined'){
6584 c.style += ' width:' + config.width + 'px;';
6585 this.totalWidth += config.width;
6587 this.totalWidth += 100; // assume minimum of 100 per column?
6590 if(typeof(config.cls) != 'undefined'){
6591 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6594 ['xs','sm','md','lg'].map(function(size){
6596 if(typeof(config[size]) == 'undefined'){
6600 if (!config[size]) { // 0 = hidden
6601 c.cls += ' hidden-' + size;
6605 c.cls += ' col-' + size + '-' + config[size];
6615 renderBody : function()
6625 colspan : this.cm.getColumnCount()
6635 renderFooter : function()
6645 colspan : this.cm.getColumnCount()
6659 // Roo.log('ds onload');
6664 var ds = this.store;
6666 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6667 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6668 if (_this.store.sortInfo) {
6670 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6671 e.select('i', true).addClass(['glyphicon-arrow-up']);
6674 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6675 e.select('i', true).addClass(['glyphicon-arrow-down']);
6680 var tbody = this.mainBody;
6682 if(ds.getCount() > 0){
6683 ds.data.each(function(d,rowIndex){
6684 var row = this.renderRow(cm, ds, rowIndex);
6686 tbody.createChild(row);
6690 if(row.cellObjects.length){
6691 Roo.each(row.cellObjects, function(r){
6692 _this.renderCellObject(r);
6699 var tfoot = this.el.select('tfoot', true).first();
6701 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6703 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6705 var total = this.ds.getTotalCount();
6707 if(this.footer.pageSize < total){
6708 this.mainFoot.show();
6712 Roo.each(this.el.select('tbody td', true).elements, function(e){
6713 e.on('mouseover', _this.onMouseover, _this);
6716 Roo.each(this.el.select('tbody td', true).elements, function(e){
6717 e.on('mouseout', _this.onMouseout, _this);
6719 this.fireEvent('rowsrendered', this);
6725 onUpdate : function(ds,record)
6727 this.refreshRow(record);
6731 onRemove : function(ds, record, index, isUpdate){
6732 if(isUpdate !== true){
6733 this.fireEvent("beforerowremoved", this, index, record);
6735 var bt = this.mainBody.dom;
6737 var rows = this.el.select('tbody > tr', true).elements;
6739 if(typeof(rows[index]) != 'undefined'){
6740 bt.removeChild(rows[index].dom);
6743 // if(bt.rows[index]){
6744 // bt.removeChild(bt.rows[index]);
6747 if(isUpdate !== true){
6748 //this.stripeRows(index);
6749 //this.syncRowHeights(index, index);
6751 this.fireEvent("rowremoved", this, index, record);
6755 onAdd : function(ds, records, rowIndex)
6757 //Roo.log('on Add called');
6758 // - note this does not handle multiple adding very well..
6759 var bt = this.mainBody.dom;
6760 for (var i =0 ; i < records.length;i++) {
6761 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6762 //Roo.log(records[i]);
6763 //Roo.log(this.store.getAt(rowIndex+i));
6764 this.insertRow(this.store, rowIndex + i, false);
6771 refreshRow : function(record){
6772 var ds = this.store, index;
6773 if(typeof record == 'number'){
6775 record = ds.getAt(index);
6777 index = ds.indexOf(record);
6779 this.insertRow(ds, index, true);
6781 this.onRemove(ds, record, index+1, true);
6783 //this.syncRowHeights(index, index);
6785 this.fireEvent("rowupdated", this, index, record);
6788 insertRow : function(dm, rowIndex, isUpdate){
6791 this.fireEvent("beforerowsinserted", this, rowIndex);
6793 //var s = this.getScrollState();
6794 var row = this.renderRow(this.cm, this.store, rowIndex);
6795 // insert before rowIndex..
6796 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6800 if(row.cellObjects.length){
6801 Roo.each(row.cellObjects, function(r){
6802 _this.renderCellObject(r);
6807 this.fireEvent("rowsinserted", this, rowIndex);
6808 //this.syncRowHeights(firstRow, lastRow);
6809 //this.stripeRows(firstRow);
6816 getRowDom : function(rowIndex)
6818 var rows = this.el.select('tbody > tr', true).elements;
6820 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6823 // returns the object tree for a tr..
6826 renderRow : function(cm, ds, rowIndex)
6828 var d = ds.getAt(rowIndex);
6832 cls : 'x-row-' + rowIndex,
6836 var cellObjects = [];
6838 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6839 var config = cm.config[i];
6841 var renderer = cm.getRenderer(i);
6845 if(typeof(renderer) !== 'undefined'){
6846 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6848 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6849 // and are rendered into the cells after the row is rendered - using the id for the element.
6851 if(typeof(value) === 'object'){
6861 rowIndex : rowIndex,
6866 this.fireEvent('rowclass', this, rowcfg);
6870 cls : rowcfg.rowClass + ' x-col-' + i,
6872 html: (typeof(value) === 'object') ? '' : value
6879 if(typeof(config.colspan) != 'undefined'){
6880 td.colspan = config.colspan;
6883 if(typeof(config.hidden) != 'undefined' && config.hidden){
6884 td.style += ' display:none;';
6887 if(typeof(config.align) != 'undefined' && config.align.length){
6888 td.style += ' text-align:' + config.align + ';';
6890 if(typeof(config.valign) != 'undefined' && config.valign.length){
6891 td.style += ' vertical-align:' + config.valign + ';';
6894 if(typeof(config.width) != 'undefined'){
6895 td.style += ' width:' + config.width + 'px;';
6898 if(typeof(config.cursor) != 'undefined'){
6899 td.style += ' cursor:' + config.cursor + ';';
6902 if(typeof(config.cls) != 'undefined'){
6903 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6906 ['xs','sm','md','lg'].map(function(size){
6908 if(typeof(config[size]) == 'undefined'){
6912 if (!config[size]) { // 0 = hidden
6913 td.cls += ' hidden-' + size;
6917 td.cls += ' col-' + size + '-' + config[size];
6925 row.cellObjects = cellObjects;
6933 onBeforeLoad : function()
6942 this.el.select('tbody', true).first().dom.innerHTML = '';
6945 * Show or hide a row.
6946 * @param {Number} rowIndex to show or hide
6947 * @param {Boolean} state hide
6949 setRowVisibility : function(rowIndex, state)
6951 var bt = this.mainBody.dom;
6953 var rows = this.el.select('tbody > tr', true).elements;
6955 if(typeof(rows[rowIndex]) == 'undefined'){
6958 rows[rowIndex].dom.style.display = state ? '' : 'none';
6962 getSelectionModel : function(){
6964 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6966 return this.selModel;
6969 * Render the Roo.bootstrap object from renderder
6971 renderCellObject : function(r)
6975 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6977 var t = r.cfg.render(r.container);
6980 Roo.each(r.cfg.cn, function(c){
6982 container: t.getChildContainer(),
6985 _this.renderCellObject(child);
6990 getRowIndex : function(row)
6994 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7005 * Returns the grid's underlying element = used by panel.Grid
7006 * @return {Element} The element
7008 getGridEl : function(){
7012 * Forces a resize - used by panel.Grid
7013 * @return {Element} The element
7015 autoSize : function()
7017 //var ctr = Roo.get(this.container.dom.parentElement);
7018 var ctr = Roo.get(this.el.dom);
7020 var thd = this.getGridEl().select('thead',true).first();
7021 var tbd = this.getGridEl().select('tbody', true).first();
7022 var tfd = this.getGridEl().select('tfoot', true).first();
7024 var cw = ctr.getWidth();
7028 tbd.setSize(ctr.getWidth(),
7029 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7031 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7034 cw = Math.max(cw, this.totalWidth);
7035 this.getGridEl().select('tr',true).setWidth(cw);
7036 // resize 'expandable coloumn?
7038 return; // we doe not have a view in this design..
7041 onBodyScroll: function()
7043 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7045 this.mainHead.setStyle({
7046 'position' : 'relative',
7047 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7053 var scrollHeight = this.mainBody.dom.scrollHeight;
7055 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7057 var height = this.mainBody.getHeight();
7059 if(scrollHeight - height == scrollTop) {
7061 var total = this.ds.getTotalCount();
7063 if(this.footer.cursor + this.footer.pageSize < total){
7065 this.footer.ds.load({
7067 start : this.footer.cursor + this.footer.pageSize,
7068 limit : this.footer.pageSize
7078 onHeaderChange : function()
7080 var header = this.renderHeader();
7081 var table = this.el.select('table', true).first();
7083 this.mainHead.remove();
7084 this.mainHead = table.createChild(header, this.mainBody, false);
7087 onHiddenChange : function(colModel, colIndex, hidden)
7089 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7090 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7092 this.CSS.updateRule(thSelector, "display", "");
7093 this.CSS.updateRule(tdSelector, "display", "");
7096 this.CSS.updateRule(thSelector, "display", "none");
7097 this.CSS.updateRule(tdSelector, "display", "none");
7100 this.onHeaderChange();
7104 setColumnWidth: function(col_index, width)
7106 // width = "md-2 xs-2..."
7107 if(!this.colModel.config[col_index]) {
7111 var w = width.split(" ");
7113 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7115 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7118 for(var j = 0; j < w.length; j++) {
7124 var size_cls = w[j].split("-");
7126 if(!Number.isInteger(size_cls[1] * 1)) {
7130 if(!this.colModel.config[col_index][size_cls[0]]) {
7134 h_row[0].classList.replace(
7135 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7136 "col-"+size_cls[0]+"-"+size_cls[1]
7139 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7142 for(var i = 0; i < rows.length; i++) {
7144 for(var j = 0; j < w.length; j++) {
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 rows[i].classList.replace(
7157 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7158 "col-"+size_cls[0]+"-"+size_cls[1]
7175 * @class Roo.bootstrap.TableCell
7176 * @extends Roo.bootstrap.Component
7177 * Bootstrap TableCell class
7178 * @cfg {String} html cell contain text
7179 * @cfg {String} cls cell class
7180 * @cfg {String} tag cell tag (td|th) default td
7181 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7182 * @cfg {String} align Aligns the content in a cell
7183 * @cfg {String} axis Categorizes cells
7184 * @cfg {String} bgcolor Specifies the background color of a cell
7185 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7186 * @cfg {Number} colspan Specifies the number of columns a cell should span
7187 * @cfg {String} headers Specifies one or more header cells a cell is related to
7188 * @cfg {Number} height Sets the height of a cell
7189 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7190 * @cfg {Number} rowspan Sets the number of rows a cell should span
7191 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7192 * @cfg {String} valign Vertical aligns the content in a cell
7193 * @cfg {Number} width Specifies the width of a cell
7196 * Create a new TableCell
7197 * @param {Object} config The config object
7200 Roo.bootstrap.TableCell = function(config){
7201 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7204 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7224 getAutoCreate : function(){
7225 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7245 cfg.align=this.align
7251 cfg.bgcolor=this.bgcolor
7254 cfg.charoff=this.charoff
7257 cfg.colspan=this.colspan
7260 cfg.headers=this.headers
7263 cfg.height=this.height
7266 cfg.nowrap=this.nowrap
7269 cfg.rowspan=this.rowspan
7272 cfg.scope=this.scope
7275 cfg.valign=this.valign
7278 cfg.width=this.width
7297 * @class Roo.bootstrap.TableRow
7298 * @extends Roo.bootstrap.Component
7299 * Bootstrap TableRow class
7300 * @cfg {String} cls row class
7301 * @cfg {String} align Aligns the content in a table row
7302 * @cfg {String} bgcolor Specifies a background color for a table row
7303 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7304 * @cfg {String} valign Vertical aligns the content in a table row
7307 * Create a new TableRow
7308 * @param {Object} config The config object
7311 Roo.bootstrap.TableRow = function(config){
7312 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7315 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7323 getAutoCreate : function(){
7324 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7334 cfg.align = this.align;
7337 cfg.bgcolor = this.bgcolor;
7340 cfg.charoff = this.charoff;
7343 cfg.valign = this.valign;
7361 * @class Roo.bootstrap.TableBody
7362 * @extends Roo.bootstrap.Component
7363 * Bootstrap TableBody class
7364 * @cfg {String} cls element class
7365 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7366 * @cfg {String} align Aligns the content inside the element
7367 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7368 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7371 * Create a new TableBody
7372 * @param {Object} config The config object
7375 Roo.bootstrap.TableBody = function(config){
7376 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7379 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7387 getAutoCreate : function(){
7388 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7402 cfg.align = this.align;
7405 cfg.charoff = this.charoff;
7408 cfg.valign = this.valign;
7415 // initEvents : function()
7422 // this.store = Roo.factory(this.store, Roo.data);
7423 // this.store.on('load', this.onLoad, this);
7425 // this.store.load();
7429 // onLoad: function ()
7431 // this.fireEvent('load', this);
7441 * Ext JS Library 1.1.1
7442 * Copyright(c) 2006-2007, Ext JS, LLC.
7444 * Originally Released Under LGPL - original licence link has changed is not relivant.
7447 * <script type="text/javascript">
7450 // as we use this in bootstrap.
7451 Roo.namespace('Roo.form');
7453 * @class Roo.form.Action
7454 * Internal Class used to handle form actions
7456 * @param {Roo.form.BasicForm} el The form element or its id
7457 * @param {Object} config Configuration options
7462 // define the action interface
7463 Roo.form.Action = function(form, options){
7465 this.options = options || {};
7468 * Client Validation Failed
7471 Roo.form.Action.CLIENT_INVALID = 'client';
7473 * Server Validation Failed
7476 Roo.form.Action.SERVER_INVALID = 'server';
7478 * Connect to Server Failed
7481 Roo.form.Action.CONNECT_FAILURE = 'connect';
7483 * Reading Data from Server Failed
7486 Roo.form.Action.LOAD_FAILURE = 'load';
7488 Roo.form.Action.prototype = {
7490 failureType : undefined,
7491 response : undefined,
7495 run : function(options){
7500 success : function(response){
7505 handleResponse : function(response){
7509 // default connection failure
7510 failure : function(response){
7512 this.response = response;
7513 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7514 this.form.afterAction(this, false);
7517 processResponse : function(response){
7518 this.response = response;
7519 if(!response.responseText){
7522 this.result = this.handleResponse(response);
7526 // utility functions used internally
7527 getUrl : function(appendParams){
7528 var url = this.options.url || this.form.url || this.form.el.dom.action;
7530 var p = this.getParams();
7532 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7538 getMethod : function(){
7539 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7542 getParams : function(){
7543 var bp = this.form.baseParams;
7544 var p = this.options.params;
7546 if(typeof p == "object"){
7547 p = Roo.urlEncode(Roo.applyIf(p, bp));
7548 }else if(typeof p == 'string' && bp){
7549 p += '&' + Roo.urlEncode(bp);
7552 p = Roo.urlEncode(bp);
7557 createCallback : function(){
7559 success: this.success,
7560 failure: this.failure,
7562 timeout: (this.form.timeout*1000),
7563 upload: this.form.fileUpload ? this.success : undefined
7568 Roo.form.Action.Submit = function(form, options){
7569 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7572 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7575 haveProgress : false,
7576 uploadComplete : false,
7578 // uploadProgress indicator.
7579 uploadProgress : function()
7581 if (!this.form.progressUrl) {
7585 if (!this.haveProgress) {
7586 Roo.MessageBox.progress("Uploading", "Uploading");
7588 if (this.uploadComplete) {
7589 Roo.MessageBox.hide();
7593 this.haveProgress = true;
7595 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7597 var c = new Roo.data.Connection();
7599 url : this.form.progressUrl,
7604 success : function(req){
7605 //console.log(data);
7609 rdata = Roo.decode(req.responseText)
7611 Roo.log("Invalid data from server..");
7615 if (!rdata || !rdata.success) {
7617 Roo.MessageBox.alert(Roo.encode(rdata));
7620 var data = rdata.data;
7622 if (this.uploadComplete) {
7623 Roo.MessageBox.hide();
7628 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7629 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7632 this.uploadProgress.defer(2000,this);
7635 failure: function(data) {
7636 Roo.log('progress url failed ');
7647 // run get Values on the form, so it syncs any secondary forms.
7648 this.form.getValues();
7650 var o = this.options;
7651 var method = this.getMethod();
7652 var isPost = method == 'POST';
7653 if(o.clientValidation === false || this.form.isValid()){
7655 if (this.form.progressUrl) {
7656 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7657 (new Date() * 1) + '' + Math.random());
7662 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7663 form:this.form.el.dom,
7664 url:this.getUrl(!isPost),
7666 params:isPost ? this.getParams() : null,
7667 isUpload: this.form.fileUpload
7670 this.uploadProgress();
7672 }else if (o.clientValidation !== false){ // client validation failed
7673 this.failureType = Roo.form.Action.CLIENT_INVALID;
7674 this.form.afterAction(this, false);
7678 success : function(response)
7680 this.uploadComplete= true;
7681 if (this.haveProgress) {
7682 Roo.MessageBox.hide();
7686 var result = this.processResponse(response);
7687 if(result === true || result.success){
7688 this.form.afterAction(this, true);
7692 this.form.markInvalid(result.errors);
7693 this.failureType = Roo.form.Action.SERVER_INVALID;
7695 this.form.afterAction(this, false);
7697 failure : function(response)
7699 this.uploadComplete= true;
7700 if (this.haveProgress) {
7701 Roo.MessageBox.hide();
7704 this.response = response;
7705 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7706 this.form.afterAction(this, false);
7709 handleResponse : function(response){
7710 if(this.form.errorReader){
7711 var rs = this.form.errorReader.read(response);
7714 for(var i = 0, len = rs.records.length; i < len; i++) {
7715 var r = rs.records[i];
7719 if(errors.length < 1){
7723 success : rs.success,
7729 ret = Roo.decode(response.responseText);
7733 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7743 Roo.form.Action.Load = function(form, options){
7744 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7745 this.reader = this.form.reader;
7748 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7753 Roo.Ajax.request(Roo.apply(
7754 this.createCallback(), {
7755 method:this.getMethod(),
7756 url:this.getUrl(false),
7757 params:this.getParams()
7761 success : function(response){
7763 var result = this.processResponse(response);
7764 if(result === true || !result.success || !result.data){
7765 this.failureType = Roo.form.Action.LOAD_FAILURE;
7766 this.form.afterAction(this, false);
7769 this.form.clearInvalid();
7770 this.form.setValues(result.data);
7771 this.form.afterAction(this, true);
7774 handleResponse : function(response){
7775 if(this.form.reader){
7776 var rs = this.form.reader.read(response);
7777 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7779 success : rs.success,
7783 return Roo.decode(response.responseText);
7787 Roo.form.Action.ACTION_TYPES = {
7788 'load' : Roo.form.Action.Load,
7789 'submit' : Roo.form.Action.Submit
7798 * @class Roo.bootstrap.Form
7799 * @extends Roo.bootstrap.Component
7800 * Bootstrap Form class
7801 * @cfg {String} method GET | POST (default POST)
7802 * @cfg {String} labelAlign top | left (default top)
7803 * @cfg {String} align left | right - for navbars
7804 * @cfg {Boolean} loadMask load mask when submit (default true)
7809 * @param {Object} config The config object
7813 Roo.bootstrap.Form = function(config){
7815 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7817 Roo.bootstrap.Form.popover.apply();
7821 * @event clientvalidation
7822 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7823 * @param {Form} this
7824 * @param {Boolean} valid true if the form has passed client-side validation
7826 clientvalidation: true,
7828 * @event beforeaction
7829 * Fires before any action is performed. Return false to cancel the action.
7830 * @param {Form} this
7831 * @param {Action} action The action to be performed
7835 * @event actionfailed
7836 * Fires when an action fails.
7837 * @param {Form} this
7838 * @param {Action} action The action that failed
7840 actionfailed : true,
7842 * @event actioncomplete
7843 * Fires when an action is completed.
7844 * @param {Form} this
7845 * @param {Action} action The action that completed
7847 actioncomplete : true
7851 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7854 * @cfg {String} method
7855 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7860 * The URL to use for form actions if one isn't supplied in the action options.
7863 * @cfg {Boolean} fileUpload
7864 * Set to true if this form is a file upload.
7868 * @cfg {Object} baseParams
7869 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7873 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7877 * @cfg {Sting} align (left|right) for navbar forms
7882 activeAction : null,
7885 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7886 * element by passing it or its id or mask the form itself by passing in true.
7889 waitMsgTarget : false,
7894 * @cfg {Boolean} errorMask (true|false) default false
7899 * @cfg {Number} maskOffset Default 100
7904 * @cfg {Boolean} maskBody
7908 getAutoCreate : function(){
7912 method : this.method || 'POST',
7913 id : this.id || Roo.id(),
7916 if (this.parent().xtype.match(/^Nav/)) {
7917 cfg.cls = 'navbar-form navbar-' + this.align;
7921 if (this.labelAlign == 'left' ) {
7922 cfg.cls += ' form-horizontal';
7928 initEvents : function()
7930 this.el.on('submit', this.onSubmit, this);
7931 // this was added as random key presses on the form where triggering form submit.
7932 this.el.on('keypress', function(e) {
7933 if (e.getCharCode() != 13) {
7936 // we might need to allow it for textareas.. and some other items.
7937 // check e.getTarget().
7939 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7943 Roo.log("keypress blocked");
7951 onSubmit : function(e){
7956 * Returns true if client-side validation on the form is successful.
7959 isValid : function(){
7960 var items = this.getItems();
7964 items.each(function(f){
7970 Roo.log('invalid field: ' + f.name);
7974 if(!target && f.el.isVisible(true)){
7980 if(this.errorMask && !valid){
7981 Roo.bootstrap.Form.popover.mask(this, target);
7988 * Returns true if any fields in this form have changed since their original load.
7991 isDirty : function(){
7993 var items = this.getItems();
7994 items.each(function(f){
8004 * Performs a predefined action (submit or load) or custom actions you define on this form.
8005 * @param {String} actionName The name of the action type
8006 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8007 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8008 * accept other config options):
8010 Property Type Description
8011 ---------------- --------------- ----------------------------------------------------------------------------------
8012 url String The url for the action (defaults to the form's url)
8013 method String The form method to use (defaults to the form's method, or POST if not defined)
8014 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8015 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8016 validate the form on the client (defaults to false)
8018 * @return {BasicForm} this
8020 doAction : function(action, options){
8021 if(typeof action == 'string'){
8022 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8024 if(this.fireEvent('beforeaction', this, action) !== false){
8025 this.beforeAction(action);
8026 action.run.defer(100, action);
8032 beforeAction : function(action){
8033 var o = action.options;
8038 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8040 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8043 // not really supported yet.. ??
8045 //if(this.waitMsgTarget === true){
8046 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8047 //}else if(this.waitMsgTarget){
8048 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8049 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8051 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8057 afterAction : function(action, success){
8058 this.activeAction = null;
8059 var o = action.options;
8064 Roo.get(document.body).unmask();
8070 //if(this.waitMsgTarget === true){
8071 // this.el.unmask();
8072 //}else if(this.waitMsgTarget){
8073 // this.waitMsgTarget.unmask();
8075 // Roo.MessageBox.updateProgress(1);
8076 // Roo.MessageBox.hide();
8083 Roo.callback(o.success, o.scope, [this, action]);
8084 this.fireEvent('actioncomplete', this, action);
8088 // failure condition..
8089 // we have a scenario where updates need confirming.
8090 // eg. if a locking scenario exists..
8091 // we look for { errors : { needs_confirm : true }} in the response.
8093 (typeof(action.result) != 'undefined') &&
8094 (typeof(action.result.errors) != 'undefined') &&
8095 (typeof(action.result.errors.needs_confirm) != 'undefined')
8098 Roo.log("not supported yet");
8101 Roo.MessageBox.confirm(
8102 "Change requires confirmation",
8103 action.result.errorMsg,
8108 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8118 Roo.callback(o.failure, o.scope, [this, action]);
8119 // show an error message if no failed handler is set..
8120 if (!this.hasListener('actionfailed')) {
8121 Roo.log("need to add dialog support");
8123 Roo.MessageBox.alert("Error",
8124 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8125 action.result.errorMsg :
8126 "Saving Failed, please check your entries or try again"
8131 this.fireEvent('actionfailed', this, action);
8136 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8137 * @param {String} id The value to search for
8140 findField : function(id){
8141 var items = this.getItems();
8142 var field = items.get(id);
8144 items.each(function(f){
8145 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8152 return field || null;
8155 * Mark fields in this form invalid in bulk.
8156 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8157 * @return {BasicForm} this
8159 markInvalid : function(errors){
8160 if(errors instanceof Array){
8161 for(var i = 0, len = errors.length; i < len; i++){
8162 var fieldError = errors[i];
8163 var f = this.findField(fieldError.id);
8165 f.markInvalid(fieldError.msg);
8171 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8172 field.markInvalid(errors[id]);
8176 //Roo.each(this.childForms || [], function (f) {
8177 // f.markInvalid(errors);
8184 * Set values for fields in this form in bulk.
8185 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8186 * @return {BasicForm} this
8188 setValues : function(values){
8189 if(values instanceof Array){ // array of objects
8190 for(var i = 0, len = values.length; i < len; i++){
8192 var f = this.findField(v.id);
8194 f.setValue(v.value);
8195 if(this.trackResetOnLoad){
8196 f.originalValue = f.getValue();
8200 }else{ // object hash
8203 if(typeof values[id] != 'function' && (field = this.findField(id))){
8205 if (field.setFromData &&
8207 field.displayField &&
8208 // combos' with local stores can
8209 // be queried via setValue()
8210 // to set their value..
8211 (field.store && !field.store.isLocal)
8215 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8216 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8217 field.setFromData(sd);
8219 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8221 field.setFromData(values);
8224 field.setValue(values[id]);
8228 if(this.trackResetOnLoad){
8229 field.originalValue = field.getValue();
8235 //Roo.each(this.childForms || [], function (f) {
8236 // f.setValues(values);
8243 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8244 * they are returned as an array.
8245 * @param {Boolean} asString
8248 getValues : function(asString){
8249 //if (this.childForms) {
8250 // copy values from the child forms
8251 // Roo.each(this.childForms, function (f) {
8252 // this.setValues(f.getValues());
8258 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8259 if(asString === true){
8262 return Roo.urlDecode(fs);
8266 * Returns the fields in this form as an object with key/value pairs.
8267 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8270 getFieldValues : function(with_hidden)
8272 var items = this.getItems();
8274 items.each(function(f){
8280 var v = f.getValue();
8282 if (f.inputType =='radio') {
8283 if (typeof(ret[f.getName()]) == 'undefined') {
8284 ret[f.getName()] = ''; // empty..
8287 if (!f.el.dom.checked) {
8295 if(f.xtype == 'MoneyField'){
8296 ret[f.currencyName] = f.getCurrency();
8299 // not sure if this supported any more..
8300 if ((typeof(v) == 'object') && f.getRawValue) {
8301 v = f.getRawValue() ; // dates..
8303 // combo boxes where name != hiddenName...
8304 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8305 ret[f.name] = f.getRawValue();
8307 ret[f.getName()] = v;
8314 * Clears all invalid messages in this form.
8315 * @return {BasicForm} this
8317 clearInvalid : function(){
8318 var items = this.getItems();
8320 items.each(function(f){
8329 * @return {BasicForm} this
8332 var items = this.getItems();
8333 items.each(function(f){
8337 Roo.each(this.childForms || [], function (f) {
8345 getItems : function()
8347 var r=new Roo.util.MixedCollection(false, function(o){
8348 return o.id || (o.id = Roo.id());
8350 var iter = function(el) {
8357 Roo.each(el.items,function(e) {
8366 hideFields : function(items)
8368 Roo.each(items, function(i){
8370 var f = this.findField(i);
8381 showFields : function(items)
8383 Roo.each(items, function(i){
8385 var f = this.findField(i);
8398 Roo.apply(Roo.bootstrap.Form, {
8425 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8426 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8427 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8428 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8431 this.maskEl.top.enableDisplayMode("block");
8432 this.maskEl.left.enableDisplayMode("block");
8433 this.maskEl.bottom.enableDisplayMode("block");
8434 this.maskEl.right.enableDisplayMode("block");
8436 this.toolTip = new Roo.bootstrap.Tooltip({
8437 cls : 'roo-form-error-popover',
8439 'left' : ['r-l', [-2,0], 'right'],
8440 'right' : ['l-r', [2,0], 'left'],
8441 'bottom' : ['tl-bl', [0,2], 'top'],
8442 'top' : [ 'bl-tl', [0,-2], 'bottom']
8446 this.toolTip.render(Roo.get(document.body));
8448 this.toolTip.el.enableDisplayMode("block");
8450 Roo.get(document.body).on('click', function(){
8454 Roo.get(document.body).on('touchstart', function(){
8458 this.isApplied = true
8461 mask : function(form, target)
8465 this.target = target;
8467 if(!this.form.errorMask || !target.el){
8471 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8473 Roo.log(scrollable);
8475 var ot = this.target.el.calcOffsetsTo(scrollable);
8477 var scrollTo = ot[1] - this.form.maskOffset;
8479 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8481 scrollable.scrollTo('top', scrollTo);
8483 var box = this.target.el.getBox();
8485 var zIndex = Roo.bootstrap.Modal.zIndex++;
8488 this.maskEl.top.setStyle('position', 'absolute');
8489 this.maskEl.top.setStyle('z-index', zIndex);
8490 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8491 this.maskEl.top.setLeft(0);
8492 this.maskEl.top.setTop(0);
8493 this.maskEl.top.show();
8495 this.maskEl.left.setStyle('position', 'absolute');
8496 this.maskEl.left.setStyle('z-index', zIndex);
8497 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8498 this.maskEl.left.setLeft(0);
8499 this.maskEl.left.setTop(box.y - this.padding);
8500 this.maskEl.left.show();
8502 this.maskEl.bottom.setStyle('position', 'absolute');
8503 this.maskEl.bottom.setStyle('z-index', zIndex);
8504 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8505 this.maskEl.bottom.setLeft(0);
8506 this.maskEl.bottom.setTop(box.bottom + this.padding);
8507 this.maskEl.bottom.show();
8509 this.maskEl.right.setStyle('position', 'absolute');
8510 this.maskEl.right.setStyle('z-index', zIndex);
8511 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8512 this.maskEl.right.setLeft(box.right + this.padding);
8513 this.maskEl.right.setTop(box.y - this.padding);
8514 this.maskEl.right.show();
8516 this.toolTip.bindEl = this.target.el;
8518 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8520 var tip = this.target.blankText;
8522 if(this.target.getValue() !== '' ) {
8524 if (this.target.invalidText.length) {
8525 tip = this.target.invalidText;
8526 } else if (this.target.regexText.length){
8527 tip = this.target.regexText;
8531 this.toolTip.show(tip);
8533 this.intervalID = window.setInterval(function() {
8534 Roo.bootstrap.Form.popover.unmask();
8537 window.onwheel = function(){ return false;};
8539 (function(){ this.isMasked = true; }).defer(500, this);
8545 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8549 this.maskEl.top.setStyle('position', 'absolute');
8550 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8551 this.maskEl.top.hide();
8553 this.maskEl.left.setStyle('position', 'absolute');
8554 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8555 this.maskEl.left.hide();
8557 this.maskEl.bottom.setStyle('position', 'absolute');
8558 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8559 this.maskEl.bottom.hide();
8561 this.maskEl.right.setStyle('position', 'absolute');
8562 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8563 this.maskEl.right.hide();
8565 this.toolTip.hide();
8567 this.toolTip.el.hide();
8569 window.onwheel = function(){ return true;};
8571 if(this.intervalID){
8572 window.clearInterval(this.intervalID);
8573 this.intervalID = false;
8576 this.isMasked = false;
8586 * Ext JS Library 1.1.1
8587 * Copyright(c) 2006-2007, Ext JS, LLC.
8589 * Originally Released Under LGPL - original licence link has changed is not relivant.
8592 * <script type="text/javascript">
8595 * @class Roo.form.VTypes
8596 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8599 Roo.form.VTypes = function(){
8600 // closure these in so they are only created once.
8601 var alpha = /^[a-zA-Z_]+$/;
8602 var alphanum = /^[a-zA-Z0-9_]+$/;
8603 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8604 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8606 // All these messages and functions are configurable
8609 * The function used to validate email addresses
8610 * @param {String} value The email address
8612 'email' : function(v){
8613 return email.test(v);
8616 * The error text to display when the email validation function returns false
8619 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8621 * The keystroke filter mask to be applied on email input
8624 'emailMask' : /[a-z0-9_\.\-@]/i,
8627 * The function used to validate URLs
8628 * @param {String} value The URL
8630 'url' : function(v){
8634 * The error text to display when the url validation function returns false
8637 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8640 * The function used to validate alpha values
8641 * @param {String} value The value
8643 'alpha' : function(v){
8644 return alpha.test(v);
8647 * The error text to display when the alpha validation function returns false
8650 'alphaText' : 'This field should only contain letters and _',
8652 * The keystroke filter mask to be applied on alpha input
8655 'alphaMask' : /[a-z_]/i,
8658 * The function used to validate alphanumeric values
8659 * @param {String} value The value
8661 'alphanum' : function(v){
8662 return alphanum.test(v);
8665 * The error text to display when the alphanumeric validation function returns false
8668 'alphanumText' : 'This field should only contain letters, numbers and _',
8670 * The keystroke filter mask to be applied on alphanumeric input
8673 'alphanumMask' : /[a-z0-9_]/i
8683 * @class Roo.bootstrap.Input
8684 * @extends Roo.bootstrap.Component
8685 * Bootstrap Input class
8686 * @cfg {Boolean} disabled is it disabled
8687 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8688 * @cfg {String} name name of the input
8689 * @cfg {string} fieldLabel - the label associated
8690 * @cfg {string} placeholder - placeholder to put in text.
8691 * @cfg {string} before - input group add on before
8692 * @cfg {string} after - input group add on after
8693 * @cfg {string} size - (lg|sm) or leave empty..
8694 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8695 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8696 * @cfg {Number} md colspan out of 12 for computer-sized screens
8697 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8698 * @cfg {string} value default value of the input
8699 * @cfg {Number} labelWidth set the width of label
8700 * @cfg {Number} labellg set the width of label (1-12)
8701 * @cfg {Number} labelmd set the width of label (1-12)
8702 * @cfg {Number} labelsm set the width of label (1-12)
8703 * @cfg {Number} labelxs set the width of label (1-12)
8704 * @cfg {String} labelAlign (top|left)
8705 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8706 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8707 * @cfg {String} indicatorpos (left|right) default left
8708 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8709 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8711 * @cfg {String} align (left|center|right) Default left
8712 * @cfg {Boolean} forceFeedback (true|false) Default false
8715 * Create a new Input
8716 * @param {Object} config The config object
8719 Roo.bootstrap.Input = function(config){
8721 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8726 * Fires when this field receives input focus.
8727 * @param {Roo.form.Field} this
8732 * Fires when this field loses input focus.
8733 * @param {Roo.form.Field} this
8738 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8739 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8740 * @param {Roo.form.Field} this
8741 * @param {Roo.EventObject} e The event object
8746 * Fires just before the field blurs if the field value has changed.
8747 * @param {Roo.form.Field} this
8748 * @param {Mixed} newValue The new value
8749 * @param {Mixed} oldValue The original value
8754 * Fires after the field has been marked as invalid.
8755 * @param {Roo.form.Field} this
8756 * @param {String} msg The validation message
8761 * Fires after the field has been validated with no errors.
8762 * @param {Roo.form.Field} this
8767 * Fires after the key up
8768 * @param {Roo.form.Field} this
8769 * @param {Roo.EventObject} e The event Object
8775 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8777 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8778 automatic validation (defaults to "keyup").
8780 validationEvent : "keyup",
8782 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8784 validateOnBlur : true,
8786 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8788 validationDelay : 250,
8790 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8792 focusClass : "x-form-focus", // not needed???
8796 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8798 invalidClass : "has-warning",
8801 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8803 validClass : "has-success",
8806 * @cfg {Boolean} hasFeedback (true|false) default true
8811 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8813 invalidFeedbackClass : "glyphicon-warning-sign",
8816 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8818 validFeedbackClass : "glyphicon-ok",
8821 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8823 selectOnFocus : false,
8826 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8830 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8835 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8837 disableKeyFilter : false,
8840 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8844 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8848 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8850 blankText : "Please complete this mandatory field",
8853 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8857 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8859 maxLength : Number.MAX_VALUE,
8861 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8863 minLengthText : "The minimum length for this field is {0}",
8865 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8867 maxLengthText : "The maximum length for this field is {0}",
8871 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8872 * If available, this function will be called only after the basic validators all return true, and will be passed the
8873 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8877 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8878 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8879 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8883 * @cfg {String} regexText -- Depricated - use Invalid Text
8888 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8894 autocomplete: false,
8913 formatedValue : false,
8914 forceFeedback : false,
8916 indicatorpos : 'left',
8926 parentLabelAlign : function()
8929 while (parent.parent()) {
8930 parent = parent.parent();
8931 if (typeof(parent.labelAlign) !='undefined') {
8932 return parent.labelAlign;
8939 getAutoCreate : function()
8941 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8947 if(this.inputType != 'hidden'){
8948 cfg.cls = 'form-group' //input-group
8954 type : this.inputType,
8956 cls : 'form-control',
8957 placeholder : this.placeholder || '',
8958 autocomplete : this.autocomplete || 'new-password'
8961 if(this.capture.length){
8962 input.capture = this.capture;
8965 if(this.accept.length){
8966 input.accept = this.accept + "/*";
8970 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8973 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8974 input.maxLength = this.maxLength;
8977 if (this.disabled) {
8978 input.disabled=true;
8981 if (this.readOnly) {
8982 input.readonly=true;
8986 input.name = this.name;
8990 input.cls += ' input-' + this.size;
8994 ['xs','sm','md','lg'].map(function(size){
8995 if (settings[size]) {
8996 cfg.cls += ' col-' + size + '-' + settings[size];
9000 var inputblock = input;
9004 cls: 'glyphicon form-control-feedback'
9007 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9010 cls : 'has-feedback',
9018 if (this.before || this.after) {
9021 cls : 'input-group',
9025 if (this.before && typeof(this.before) == 'string') {
9027 inputblock.cn.push({
9029 cls : 'roo-input-before input-group-addon',
9033 if (this.before && typeof(this.before) == 'object') {
9034 this.before = Roo.factory(this.before);
9036 inputblock.cn.push({
9038 cls : 'roo-input-before input-group-' +
9039 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9043 inputblock.cn.push(input);
9045 if (this.after && typeof(this.after) == 'string') {
9046 inputblock.cn.push({
9048 cls : 'roo-input-after input-group-addon',
9052 if (this.after && typeof(this.after) == 'object') {
9053 this.after = Roo.factory(this.after);
9055 inputblock.cn.push({
9057 cls : 'roo-input-after input-group-' +
9058 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9062 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9063 inputblock.cls += ' has-feedback';
9064 inputblock.cn.push(feedback);
9068 if (align ==='left' && this.fieldLabel.length) {
9070 cfg.cls += ' roo-form-group-label-left';
9075 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9076 tooltip : 'This field is required'
9081 cls : 'control-label',
9082 html : this.fieldLabel
9093 var labelCfg = cfg.cn[1];
9094 var contentCfg = cfg.cn[2];
9096 if(this.indicatorpos == 'right'){
9101 cls : 'control-label',
9105 html : this.fieldLabel
9109 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9110 tooltip : 'This field is required'
9123 labelCfg = cfg.cn[0];
9124 contentCfg = cfg.cn[1];
9128 if(this.labelWidth > 12){
9129 labelCfg.style = "width: " + this.labelWidth + 'px';
9132 if(this.labelWidth < 13 && this.labelmd == 0){
9133 this.labelmd = this.labelWidth;
9136 if(this.labellg > 0){
9137 labelCfg.cls += ' col-lg-' + this.labellg;
9138 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9141 if(this.labelmd > 0){
9142 labelCfg.cls += ' col-md-' + this.labelmd;
9143 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9146 if(this.labelsm > 0){
9147 labelCfg.cls += ' col-sm-' + this.labelsm;
9148 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9151 if(this.labelxs > 0){
9152 labelCfg.cls += ' col-xs-' + this.labelxs;
9153 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9157 } else if ( this.fieldLabel.length) {
9162 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9163 tooltip : 'This field is required'
9167 //cls : 'input-group-addon',
9168 html : this.fieldLabel
9176 if(this.indicatorpos == 'right'){
9181 //cls : 'input-group-addon',
9182 html : this.fieldLabel
9187 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9188 tooltip : 'This field is required'
9208 if (this.parentType === 'Navbar' && this.parent().bar) {
9209 cfg.cls += ' navbar-form';
9212 if (this.parentType === 'NavGroup') {
9213 cfg.cls += ' navbar-form';
9221 * return the real input element.
9223 inputEl: function ()
9225 return this.el.select('input.form-control',true).first();
9228 tooltipEl : function()
9230 return this.inputEl();
9233 indicatorEl : function()
9235 var indicator = this.el.select('i.roo-required-indicator',true).first();
9245 setDisabled : function(v)
9247 var i = this.inputEl().dom;
9249 i.removeAttribute('disabled');
9253 i.setAttribute('disabled','true');
9255 initEvents : function()
9258 this.inputEl().on("keydown" , this.fireKey, this);
9259 this.inputEl().on("focus", this.onFocus, this);
9260 this.inputEl().on("blur", this.onBlur, this);
9262 this.inputEl().relayEvent('keyup', this);
9264 this.indicator = this.indicatorEl();
9267 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9270 // reference to original value for reset
9271 this.originalValue = this.getValue();
9272 //Roo.form.TextField.superclass.initEvents.call(this);
9273 if(this.validationEvent == 'keyup'){
9274 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9275 this.inputEl().on('keyup', this.filterValidation, this);
9277 else if(this.validationEvent !== false){
9278 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9281 if(this.selectOnFocus){
9282 this.on("focus", this.preFocus, this);
9285 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9286 this.inputEl().on("keypress", this.filterKeys, this);
9288 this.inputEl().relayEvent('keypress', this);
9291 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9292 this.el.on("click", this.autoSize, this);
9295 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9296 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9299 if (typeof(this.before) == 'object') {
9300 this.before.render(this.el.select('.roo-input-before',true).first());
9302 if (typeof(this.after) == 'object') {
9303 this.after.render(this.el.select('.roo-input-after',true).first());
9306 this.inputEl().on('change', this.onChange, this);
9309 filterValidation : function(e){
9310 if(!e.isNavKeyPress()){
9311 this.validationTask.delay(this.validationDelay);
9315 * Validates the field value
9316 * @return {Boolean} True if the value is valid, else false
9318 validate : function(){
9319 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9320 if(this.disabled || this.validateValue(this.getRawValue())){
9331 * Validates a value according to the field's validation rules and marks the field as invalid
9332 * if the validation fails
9333 * @param {Mixed} value The value to validate
9334 * @return {Boolean} True if the value is valid, else false
9336 validateValue : function(value)
9338 if(this.getVisibilityEl().hasClass('hidden')){
9342 if(value.length < 1) { // if it's blank
9343 if(this.allowBlank){
9349 if(value.length < this.minLength){
9352 if(value.length > this.maxLength){
9356 var vt = Roo.form.VTypes;
9357 if(!vt[this.vtype](value, this)){
9361 if(typeof this.validator == "function"){
9362 var msg = this.validator(value);
9366 if (typeof(msg) == 'string') {
9367 this.invalidText = msg;
9371 if(this.regex && !this.regex.test(value)){
9379 fireKey : function(e){
9380 //Roo.log('field ' + e.getKey());
9381 if(e.isNavKeyPress()){
9382 this.fireEvent("specialkey", this, e);
9385 focus : function (selectText){
9387 this.inputEl().focus();
9388 if(selectText === true){
9389 this.inputEl().dom.select();
9395 onFocus : function(){
9396 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9397 // this.el.addClass(this.focusClass);
9400 this.hasFocus = true;
9401 this.startValue = this.getValue();
9402 this.fireEvent("focus", this);
9406 beforeBlur : Roo.emptyFn,
9410 onBlur : function(){
9412 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9413 //this.el.removeClass(this.focusClass);
9415 this.hasFocus = false;
9416 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9419 var v = this.getValue();
9420 if(String(v) !== String(this.startValue)){
9421 this.fireEvent('change', this, v, this.startValue);
9423 this.fireEvent("blur", this);
9426 onChange : function(e)
9428 var v = this.getValue();
9429 if(String(v) !== String(this.startValue)){
9430 this.fireEvent('change', this, v, this.startValue);
9436 * Resets the current field value to the originally loaded value and clears any validation messages
9439 this.setValue(this.originalValue);
9443 * Returns the name of the field
9444 * @return {Mixed} name The name field
9446 getName: function(){
9450 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9451 * @return {Mixed} value The field value
9453 getValue : function(){
9455 var v = this.inputEl().getValue();
9460 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9461 * @return {Mixed} value The field value
9463 getRawValue : function(){
9464 var v = this.inputEl().getValue();
9470 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9471 * @param {Mixed} value The value to set
9473 setRawValue : function(v){
9474 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9477 selectText : function(start, end){
9478 var v = this.getRawValue();
9480 start = start === undefined ? 0 : start;
9481 end = end === undefined ? v.length : end;
9482 var d = this.inputEl().dom;
9483 if(d.setSelectionRange){
9484 d.setSelectionRange(start, end);
9485 }else if(d.createTextRange){
9486 var range = d.createTextRange();
9487 range.moveStart("character", start);
9488 range.moveEnd("character", v.length-end);
9495 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9496 * @param {Mixed} value The value to set
9498 setValue : function(v){
9501 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9507 processValue : function(value){
9508 if(this.stripCharsRe){
9509 var newValue = value.replace(this.stripCharsRe, '');
9510 if(newValue !== value){
9511 this.setRawValue(newValue);
9518 preFocus : function(){
9520 if(this.selectOnFocus){
9521 this.inputEl().dom.select();
9524 filterKeys : function(e){
9526 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9529 var c = e.getCharCode(), cc = String.fromCharCode(c);
9530 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9533 if(!this.maskRe.test(cc)){
9538 * Clear any invalid styles/messages for this field
9540 clearInvalid : function(){
9542 if(!this.el || this.preventMark){ // not rendered
9547 this.el.removeClass(this.invalidClass);
9549 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9551 var feedback = this.el.select('.form-control-feedback', true).first();
9554 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9560 this.indicator.removeClass('visible');
9561 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9564 this.fireEvent('valid', this);
9568 * Mark this field as valid
9570 markValid : function()
9572 if(!this.el || this.preventMark){ // not rendered...
9576 this.el.removeClass([this.invalidClass, this.validClass]);
9578 var feedback = this.el.select('.form-control-feedback', true).first();
9581 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9585 this.indicator.removeClass('visible');
9586 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9593 if(this.allowBlank && !this.getRawValue().length){
9597 this.el.addClass(this.validClass);
9599 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9601 var feedback = this.el.select('.form-control-feedback', true).first();
9604 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9605 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9610 this.fireEvent('valid', this);
9614 * Mark this field as invalid
9615 * @param {String} msg The validation message
9617 markInvalid : function(msg)
9619 if(!this.el || this.preventMark){ // not rendered
9623 this.el.removeClass([this.invalidClass, this.validClass]);
9625 var feedback = this.el.select('.form-control-feedback', true).first();
9628 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9635 if(this.allowBlank && !this.getRawValue().length){
9640 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9641 this.indicator.addClass('visible');
9644 this.el.addClass(this.invalidClass);
9646 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9648 var feedback = this.el.select('.form-control-feedback', true).first();
9651 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9653 if(this.getValue().length || this.forceFeedback){
9654 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9661 this.fireEvent('invalid', this, msg);
9664 SafariOnKeyDown : function(event)
9666 // this is a workaround for a password hang bug on chrome/ webkit.
9667 if (this.inputEl().dom.type != 'password') {
9671 var isSelectAll = false;
9673 if(this.inputEl().dom.selectionEnd > 0){
9674 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9676 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9677 event.preventDefault();
9682 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9684 event.preventDefault();
9685 // this is very hacky as keydown always get's upper case.
9687 var cc = String.fromCharCode(event.getCharCode());
9688 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9692 adjustWidth : function(tag, w){
9693 tag = tag.toLowerCase();
9694 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9695 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9699 if(tag == 'textarea'){
9702 }else if(Roo.isOpera){
9706 if(tag == 'textarea'){
9714 setFieldLabel : function(v)
9721 var ar = this.el.select('label > span',true);
9723 if (ar.elements.length) {
9724 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9725 this.fieldLabel = v;
9729 var br = this.el.select('label',true);
9731 if(br.elements.length) {
9732 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9733 this.fieldLabel = v;
9737 Roo.log('Cannot Found any of label > span || label in input');
9741 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9742 this.fieldLabel = v;
9757 * @class Roo.bootstrap.TextArea
9758 * @extends Roo.bootstrap.Input
9759 * Bootstrap TextArea class
9760 * @cfg {Number} cols Specifies the visible width of a text area
9761 * @cfg {Number} rows Specifies the visible number of lines in a text area
9762 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9763 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9764 * @cfg {string} html text
9767 * Create a new TextArea
9768 * @param {Object} config The config object
9771 Roo.bootstrap.TextArea = function(config){
9772 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9776 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9786 getAutoCreate : function(){
9788 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9794 if(this.inputType != 'hidden'){
9795 cfg.cls = 'form-group' //input-group
9803 value : this.value || '',
9804 html: this.html || '',
9805 cls : 'form-control',
9806 placeholder : this.placeholder || ''
9810 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9811 input.maxLength = this.maxLength;
9815 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9819 input.cols = this.cols;
9822 if (this.readOnly) {
9823 input.readonly = true;
9827 input.name = this.name;
9831 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9835 ['xs','sm','md','lg'].map(function(size){
9836 if (settings[size]) {
9837 cfg.cls += ' col-' + size + '-' + settings[size];
9841 var inputblock = input;
9843 if(this.hasFeedback && !this.allowBlank){
9847 cls: 'glyphicon form-control-feedback'
9851 cls : 'has-feedback',
9860 if (this.before || this.after) {
9863 cls : 'input-group',
9867 inputblock.cn.push({
9869 cls : 'input-group-addon',
9874 inputblock.cn.push(input);
9876 if(this.hasFeedback && !this.allowBlank){
9877 inputblock.cls += ' has-feedback';
9878 inputblock.cn.push(feedback);
9882 inputblock.cn.push({
9884 cls : 'input-group-addon',
9891 if (align ==='left' && this.fieldLabel.length) {
9896 cls : 'control-label',
9897 html : this.fieldLabel
9908 if(this.labelWidth > 12){
9909 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9912 if(this.labelWidth < 13 && this.labelmd == 0){
9913 this.labelmd = this.labelWidth;
9916 if(this.labellg > 0){
9917 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9918 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9921 if(this.labelmd > 0){
9922 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9923 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9926 if(this.labelsm > 0){
9927 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9928 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9931 if(this.labelxs > 0){
9932 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9933 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9936 } else if ( this.fieldLabel.length) {
9941 //cls : 'input-group-addon',
9942 html : this.fieldLabel
9960 if (this.disabled) {
9961 input.disabled=true;
9968 * return the real textarea element.
9970 inputEl: function ()
9972 return this.el.select('textarea.form-control',true).first();
9976 * Clear any invalid styles/messages for this field
9978 clearInvalid : function()
9981 if(!this.el || this.preventMark){ // not rendered
9985 var label = this.el.select('label', true).first();
9986 var icon = this.el.select('i.fa-star', true).first();
9992 this.el.removeClass(this.invalidClass);
9994 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9996 var feedback = this.el.select('.form-control-feedback', true).first();
9999 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10004 this.fireEvent('valid', this);
10008 * Mark this field as valid
10010 markValid : function()
10012 if(!this.el || this.preventMark){ // not rendered
10016 this.el.removeClass([this.invalidClass, this.validClass]);
10018 var feedback = this.el.select('.form-control-feedback', true).first();
10021 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10024 if(this.disabled || this.allowBlank){
10028 var label = this.el.select('label', true).first();
10029 var icon = this.el.select('i.fa-star', true).first();
10035 this.el.addClass(this.validClass);
10037 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10039 var feedback = this.el.select('.form-control-feedback', true).first();
10042 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10043 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10048 this.fireEvent('valid', this);
10052 * Mark this field as invalid
10053 * @param {String} msg The validation message
10055 markInvalid : function(msg)
10057 if(!this.el || this.preventMark){ // not rendered
10061 this.el.removeClass([this.invalidClass, this.validClass]);
10063 var feedback = this.el.select('.form-control-feedback', true).first();
10066 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10069 if(this.disabled || this.allowBlank){
10073 var label = this.el.select('label', true).first();
10074 var icon = this.el.select('i.fa-star', true).first();
10076 if(!this.getValue().length && label && !icon){
10077 this.el.createChild({
10079 cls : 'text-danger fa fa-lg fa-star',
10080 tooltip : 'This field is required',
10081 style : 'margin-right:5px;'
10085 this.el.addClass(this.invalidClass);
10087 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10089 var feedback = this.el.select('.form-control-feedback', true).first();
10092 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10094 if(this.getValue().length || this.forceFeedback){
10095 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10102 this.fireEvent('invalid', this, msg);
10110 * trigger field - base class for combo..
10115 * @class Roo.bootstrap.TriggerField
10116 * @extends Roo.bootstrap.Input
10117 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10118 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10119 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10120 * for which you can provide a custom implementation. For example:
10122 var trigger = new Roo.bootstrap.TriggerField();
10123 trigger.onTriggerClick = myTriggerFn;
10124 trigger.applyTo('my-field');
10127 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10128 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10129 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10130 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10131 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10134 * Create a new TriggerField.
10135 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10136 * to the base TextField)
10138 Roo.bootstrap.TriggerField = function(config){
10139 this.mimicing = false;
10140 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10143 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10145 * @cfg {String} triggerClass A CSS class to apply to the trigger
10148 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10153 * @cfg {Boolean} removable (true|false) special filter default false
10157 /** @cfg {Boolean} grow @hide */
10158 /** @cfg {Number} growMin @hide */
10159 /** @cfg {Number} growMax @hide */
10165 autoSize: Roo.emptyFn,
10169 deferHeight : true,
10172 actionMode : 'wrap',
10177 getAutoCreate : function(){
10179 var align = this.labelAlign || this.parentLabelAlign();
10184 cls: 'form-group' //input-group
10191 type : this.inputType,
10192 cls : 'form-control',
10193 autocomplete: 'new-password',
10194 placeholder : this.placeholder || ''
10198 input.name = this.name;
10201 input.cls += ' input-' + this.size;
10204 if (this.disabled) {
10205 input.disabled=true;
10208 var inputblock = input;
10210 if(this.hasFeedback && !this.allowBlank){
10214 cls: 'glyphicon form-control-feedback'
10217 if(this.removable && !this.editable && !this.tickable){
10219 cls : 'has-feedback',
10225 cls : 'roo-combo-removable-btn close'
10232 cls : 'has-feedback',
10241 if(this.removable && !this.editable && !this.tickable){
10243 cls : 'roo-removable',
10249 cls : 'roo-combo-removable-btn close'
10256 if (this.before || this.after) {
10259 cls : 'input-group',
10263 inputblock.cn.push({
10265 cls : 'input-group-addon',
10270 inputblock.cn.push(input);
10272 if(this.hasFeedback && !this.allowBlank){
10273 inputblock.cls += ' has-feedback';
10274 inputblock.cn.push(feedback);
10278 inputblock.cn.push({
10280 cls : 'input-group-addon',
10293 cls: 'form-hidden-field'
10307 cls: 'form-hidden-field'
10311 cls: 'roo-select2-choices',
10315 cls: 'roo-select2-search-field',
10328 cls: 'roo-select2-container input-group',
10333 // cls: 'typeahead typeahead-long dropdown-menu',
10334 // style: 'display:none'
10339 if(!this.multiple && this.showToggleBtn){
10345 if (this.caret != false) {
10348 cls: 'fa fa-' + this.caret
10355 cls : 'input-group-addon btn dropdown-toggle',
10360 cls: 'combobox-clear',
10374 combobox.cls += ' roo-select2-container-multi';
10377 if (align ==='left' && this.fieldLabel.length) {
10379 cfg.cls += ' roo-form-group-label-left';
10384 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10385 tooltip : 'This field is required'
10390 cls : 'control-label',
10391 html : this.fieldLabel
10403 var labelCfg = cfg.cn[1];
10404 var contentCfg = cfg.cn[2];
10406 if(this.indicatorpos == 'right'){
10411 cls : 'control-label',
10415 html : this.fieldLabel
10419 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10420 tooltip : 'This field is required'
10433 labelCfg = cfg.cn[0];
10434 contentCfg = cfg.cn[1];
10437 if(this.labelWidth > 12){
10438 labelCfg.style = "width: " + this.labelWidth + 'px';
10441 if(this.labelWidth < 13 && this.labelmd == 0){
10442 this.labelmd = this.labelWidth;
10445 if(this.labellg > 0){
10446 labelCfg.cls += ' col-lg-' + this.labellg;
10447 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10450 if(this.labelmd > 0){
10451 labelCfg.cls += ' col-md-' + this.labelmd;
10452 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10455 if(this.labelsm > 0){
10456 labelCfg.cls += ' col-sm-' + this.labelsm;
10457 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10460 if(this.labelxs > 0){
10461 labelCfg.cls += ' col-xs-' + this.labelxs;
10462 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10465 } else if ( this.fieldLabel.length) {
10466 // Roo.log(" label");
10470 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10471 tooltip : 'This field is required'
10475 //cls : 'input-group-addon',
10476 html : this.fieldLabel
10484 if(this.indicatorpos == 'right'){
10492 html : this.fieldLabel
10496 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10497 tooltip : 'This field is required'
10510 // Roo.log(" no label && no align");
10517 ['xs','sm','md','lg'].map(function(size){
10518 if (settings[size]) {
10519 cfg.cls += ' col-' + size + '-' + settings[size];
10530 onResize : function(w, h){
10531 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10532 // if(typeof w == 'number'){
10533 // var x = w - this.trigger.getWidth();
10534 // this.inputEl().setWidth(this.adjustWidth('input', x));
10535 // this.trigger.setStyle('left', x+'px');
10540 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10543 getResizeEl : function(){
10544 return this.inputEl();
10548 getPositionEl : function(){
10549 return this.inputEl();
10553 alignErrorIcon : function(){
10554 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10558 initEvents : function(){
10562 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10563 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10564 if(!this.multiple && this.showToggleBtn){
10565 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10566 if(this.hideTrigger){
10567 this.trigger.setDisplayed(false);
10569 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10573 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10576 if(this.removable && !this.editable && !this.tickable){
10577 var close = this.closeTriggerEl();
10580 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10581 close.on('click', this.removeBtnClick, this, close);
10585 //this.trigger.addClassOnOver('x-form-trigger-over');
10586 //this.trigger.addClassOnClick('x-form-trigger-click');
10589 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10593 closeTriggerEl : function()
10595 var close = this.el.select('.roo-combo-removable-btn', true).first();
10596 return close ? close : false;
10599 removeBtnClick : function(e, h, el)
10601 e.preventDefault();
10603 if(this.fireEvent("remove", this) !== false){
10605 this.fireEvent("afterremove", this)
10609 createList : function()
10611 this.list = Roo.get(document.body).createChild({
10613 cls: 'typeahead typeahead-long dropdown-menu',
10614 style: 'display:none'
10617 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10622 initTrigger : function(){
10627 onDestroy : function(){
10629 this.trigger.removeAllListeners();
10630 // this.trigger.remove();
10633 // this.wrap.remove();
10635 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10639 onFocus : function(){
10640 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10642 if(!this.mimicing){
10643 this.wrap.addClass('x-trigger-wrap-focus');
10644 this.mimicing = true;
10645 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10646 if(this.monitorTab){
10647 this.el.on("keydown", this.checkTab, this);
10654 checkTab : function(e){
10655 if(e.getKey() == e.TAB){
10656 this.triggerBlur();
10661 onBlur : function(){
10666 mimicBlur : function(e, t){
10668 if(!this.wrap.contains(t) && this.validateBlur()){
10669 this.triggerBlur();
10675 triggerBlur : function(){
10676 this.mimicing = false;
10677 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10678 if(this.monitorTab){
10679 this.el.un("keydown", this.checkTab, this);
10681 //this.wrap.removeClass('x-trigger-wrap-focus');
10682 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10686 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10687 validateBlur : function(e, t){
10692 onDisable : function(){
10693 this.inputEl().dom.disabled = true;
10694 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10696 // this.wrap.addClass('x-item-disabled');
10701 onEnable : function(){
10702 this.inputEl().dom.disabled = false;
10703 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10705 // this.el.removeClass('x-item-disabled');
10710 onShow : function(){
10711 var ae = this.getActionEl();
10714 ae.dom.style.display = '';
10715 ae.dom.style.visibility = 'visible';
10721 onHide : function(){
10722 var ae = this.getActionEl();
10723 ae.dom.style.display = 'none';
10727 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10728 * by an implementing function.
10730 * @param {EventObject} e
10732 onTriggerClick : Roo.emptyFn
10736 * Ext JS Library 1.1.1
10737 * Copyright(c) 2006-2007, Ext JS, LLC.
10739 * Originally Released Under LGPL - original licence link has changed is not relivant.
10742 * <script type="text/javascript">
10747 * @class Roo.data.SortTypes
10749 * Defines the default sorting (casting?) comparison functions used when sorting data.
10751 Roo.data.SortTypes = {
10753 * Default sort that does nothing
10754 * @param {Mixed} s The value being converted
10755 * @return {Mixed} The comparison value
10757 none : function(s){
10762 * The regular expression used to strip tags
10766 stripTagsRE : /<\/?[^>]+>/gi,
10769 * Strips all HTML tags to sort on text only
10770 * @param {Mixed} s The value being converted
10771 * @return {String} The comparison value
10773 asText : function(s){
10774 return String(s).replace(this.stripTagsRE, "");
10778 * Strips all HTML tags to sort on text only - Case insensitive
10779 * @param {Mixed} s The value being converted
10780 * @return {String} The comparison value
10782 asUCText : function(s){
10783 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10787 * Case insensitive string
10788 * @param {Mixed} s The value being converted
10789 * @return {String} The comparison value
10791 asUCString : function(s) {
10792 return String(s).toUpperCase();
10797 * @param {Mixed} s The value being converted
10798 * @return {Number} The comparison value
10800 asDate : function(s) {
10804 if(s instanceof Date){
10805 return s.getTime();
10807 return Date.parse(String(s));
10812 * @param {Mixed} s The value being converted
10813 * @return {Float} The comparison value
10815 asFloat : function(s) {
10816 var val = parseFloat(String(s).replace(/,/g, ""));
10825 * @param {Mixed} s The value being converted
10826 * @return {Number} The comparison value
10828 asInt : function(s) {
10829 var val = parseInt(String(s).replace(/,/g, ""));
10837 * Ext JS Library 1.1.1
10838 * Copyright(c) 2006-2007, Ext JS, LLC.
10840 * Originally Released Under LGPL - original licence link has changed is not relivant.
10843 * <script type="text/javascript">
10847 * @class Roo.data.Record
10848 * Instances of this class encapsulate both record <em>definition</em> information, and record
10849 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10850 * to access Records cached in an {@link Roo.data.Store} object.<br>
10852 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10853 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10856 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10858 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10859 * {@link #create}. The parameters are the same.
10860 * @param {Array} data An associative Array of data values keyed by the field name.
10861 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10862 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10863 * not specified an integer id is generated.
10865 Roo.data.Record = function(data, id){
10866 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10871 * Generate a constructor for a specific record layout.
10872 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10873 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10874 * Each field definition object may contain the following properties: <ul>
10875 * <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,
10876 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10877 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10878 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10879 * is being used, then this is a string containing the javascript expression to reference the data relative to
10880 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10881 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10882 * this may be omitted.</p></li>
10883 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10884 * <ul><li>auto (Default, implies no conversion)</li>
10889 * <li>date</li></ul></p></li>
10890 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10891 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10892 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10893 * by the Reader into an object that will be stored in the Record. It is passed the
10894 * following parameters:<ul>
10895 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10897 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10899 * <br>usage:<br><pre><code>
10900 var TopicRecord = Roo.data.Record.create(
10901 {name: 'title', mapping: 'topic_title'},
10902 {name: 'author', mapping: 'username'},
10903 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10904 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10905 {name: 'lastPoster', mapping: 'user2'},
10906 {name: 'excerpt', mapping: 'post_text'}
10909 var myNewRecord = new TopicRecord({
10910 title: 'Do my job please',
10913 lastPost: new Date(),
10914 lastPoster: 'Animal',
10915 excerpt: 'No way dude!'
10917 myStore.add(myNewRecord);
10922 Roo.data.Record.create = function(o){
10923 var f = function(){
10924 f.superclass.constructor.apply(this, arguments);
10926 Roo.extend(f, Roo.data.Record);
10927 var p = f.prototype;
10928 p.fields = new Roo.util.MixedCollection(false, function(field){
10931 for(var i = 0, len = o.length; i < len; i++){
10932 p.fields.add(new Roo.data.Field(o[i]));
10934 f.getField = function(name){
10935 return p.fields.get(name);
10940 Roo.data.Record.AUTO_ID = 1000;
10941 Roo.data.Record.EDIT = 'edit';
10942 Roo.data.Record.REJECT = 'reject';
10943 Roo.data.Record.COMMIT = 'commit';
10945 Roo.data.Record.prototype = {
10947 * Readonly flag - true if this record has been modified.
10956 join : function(store){
10957 this.store = store;
10961 * Set the named field to the specified value.
10962 * @param {String} name The name of the field to set.
10963 * @param {Object} value The value to set the field to.
10965 set : function(name, value){
10966 if(this.data[name] == value){
10970 if(!this.modified){
10971 this.modified = {};
10973 if(typeof this.modified[name] == 'undefined'){
10974 this.modified[name] = this.data[name];
10976 this.data[name] = value;
10977 if(!this.editing && this.store){
10978 this.store.afterEdit(this);
10983 * Get the value of the named field.
10984 * @param {String} name The name of the field to get the value of.
10985 * @return {Object} The value of the field.
10987 get : function(name){
10988 return this.data[name];
10992 beginEdit : function(){
10993 this.editing = true;
10994 this.modified = {};
10998 cancelEdit : function(){
10999 this.editing = false;
11000 delete this.modified;
11004 endEdit : function(){
11005 this.editing = false;
11006 if(this.dirty && this.store){
11007 this.store.afterEdit(this);
11012 * Usually called by the {@link Roo.data.Store} which owns the Record.
11013 * Rejects all changes made to the Record since either creation, or the last commit operation.
11014 * Modified fields are reverted to their original values.
11016 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11017 * of reject operations.
11019 reject : function(){
11020 var m = this.modified;
11022 if(typeof m[n] != "function"){
11023 this.data[n] = m[n];
11026 this.dirty = false;
11027 delete this.modified;
11028 this.editing = false;
11030 this.store.afterReject(this);
11035 * Usually called by the {@link Roo.data.Store} which owns the Record.
11036 * Commits all changes made to the Record since either creation, or the last commit operation.
11038 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11039 * of commit operations.
11041 commit : function(){
11042 this.dirty = false;
11043 delete this.modified;
11044 this.editing = false;
11046 this.store.afterCommit(this);
11051 hasError : function(){
11052 return this.error != null;
11056 clearError : function(){
11061 * Creates a copy of this record.
11062 * @param {String} id (optional) A new record id if you don't want to use this record's id
11065 copy : function(newId) {
11066 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11070 * Ext JS Library 1.1.1
11071 * Copyright(c) 2006-2007, Ext JS, LLC.
11073 * Originally Released Under LGPL - original licence link has changed is not relivant.
11076 * <script type="text/javascript">
11082 * @class Roo.data.Store
11083 * @extends Roo.util.Observable
11084 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11085 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11087 * 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
11088 * has no knowledge of the format of the data returned by the Proxy.<br>
11090 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11091 * instances from the data object. These records are cached and made available through accessor functions.
11093 * Creates a new Store.
11094 * @param {Object} config A config object containing the objects needed for the Store to access data,
11095 * and read the data into Records.
11097 Roo.data.Store = function(config){
11098 this.data = new Roo.util.MixedCollection(false);
11099 this.data.getKey = function(o){
11102 this.baseParams = {};
11104 this.paramNames = {
11109 "multisort" : "_multisort"
11112 if(config && config.data){
11113 this.inlineData = config.data;
11114 delete config.data;
11117 Roo.apply(this, config);
11119 if(this.reader){ // reader passed
11120 this.reader = Roo.factory(this.reader, Roo.data);
11121 this.reader.xmodule = this.xmodule || false;
11122 if(!this.recordType){
11123 this.recordType = this.reader.recordType;
11125 if(this.reader.onMetaChange){
11126 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11130 if(this.recordType){
11131 this.fields = this.recordType.prototype.fields;
11133 this.modified = [];
11137 * @event datachanged
11138 * Fires when the data cache has changed, and a widget which is using this Store
11139 * as a Record cache should refresh its view.
11140 * @param {Store} this
11142 datachanged : true,
11144 * @event metachange
11145 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11146 * @param {Store} this
11147 * @param {Object} meta The JSON metadata
11152 * Fires when Records have been added to the Store
11153 * @param {Store} this
11154 * @param {Roo.data.Record[]} records The array of Records added
11155 * @param {Number} index The index at which the record(s) were added
11160 * Fires when a Record has been removed from the Store
11161 * @param {Store} this
11162 * @param {Roo.data.Record} record The Record that was removed
11163 * @param {Number} index The index at which the record was removed
11168 * Fires when a Record has been updated
11169 * @param {Store} this
11170 * @param {Roo.data.Record} record The Record that was updated
11171 * @param {String} operation The update operation being performed. Value may be one of:
11173 Roo.data.Record.EDIT
11174 Roo.data.Record.REJECT
11175 Roo.data.Record.COMMIT
11181 * Fires when the data cache has been cleared.
11182 * @param {Store} this
11186 * @event beforeload
11187 * Fires before a request is made for a new data object. If the beforeload handler returns false
11188 * the load action will be canceled.
11189 * @param {Store} this
11190 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11194 * @event beforeloadadd
11195 * Fires after a new set of Records has been loaded.
11196 * @param {Store} this
11197 * @param {Roo.data.Record[]} records The Records that were loaded
11198 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11200 beforeloadadd : true,
11203 * Fires after a new set of Records has been loaded, before they are added to the store.
11204 * @param {Store} this
11205 * @param {Roo.data.Record[]} records The Records that were loaded
11206 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11207 * @params {Object} return from reader
11211 * @event loadexception
11212 * Fires if an exception occurs in the Proxy during loading.
11213 * Called with the signature of the Proxy's "loadexception" event.
11214 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11217 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11218 * @param {Object} load options
11219 * @param {Object} jsonData from your request (normally this contains the Exception)
11221 loadexception : true
11225 this.proxy = Roo.factory(this.proxy, Roo.data);
11226 this.proxy.xmodule = this.xmodule || false;
11227 this.relayEvents(this.proxy, ["loadexception"]);
11229 this.sortToggle = {};
11230 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11232 Roo.data.Store.superclass.constructor.call(this);
11234 if(this.inlineData){
11235 this.loadData(this.inlineData);
11236 delete this.inlineData;
11240 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11242 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11243 * without a remote query - used by combo/forms at present.
11247 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11250 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11253 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11254 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11257 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11258 * on any HTTP request
11261 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11264 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11268 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11269 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11271 remoteSort : false,
11274 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11275 * loaded or when a record is removed. (defaults to false).
11277 pruneModifiedRecords : false,
11280 lastOptions : null,
11283 * Add Records to the Store and fires the add event.
11284 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11286 add : function(records){
11287 records = [].concat(records);
11288 for(var i = 0, len = records.length; i < len; i++){
11289 records[i].join(this);
11291 var index = this.data.length;
11292 this.data.addAll(records);
11293 this.fireEvent("add", this, records, index);
11297 * Remove a Record from the Store and fires the remove event.
11298 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11300 remove : function(record){
11301 var index = this.data.indexOf(record);
11302 this.data.removeAt(index);
11304 if(this.pruneModifiedRecords){
11305 this.modified.remove(record);
11307 this.fireEvent("remove", this, record, index);
11311 * Remove all Records from the Store and fires the clear event.
11313 removeAll : function(){
11315 if(this.pruneModifiedRecords){
11316 this.modified = [];
11318 this.fireEvent("clear", this);
11322 * Inserts Records to the Store at the given index and fires the add event.
11323 * @param {Number} index The start index at which to insert the passed Records.
11324 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11326 insert : function(index, records){
11327 records = [].concat(records);
11328 for(var i = 0, len = records.length; i < len; i++){
11329 this.data.insert(index, records[i]);
11330 records[i].join(this);
11332 this.fireEvent("add", this, records, index);
11336 * Get the index within the cache of the passed Record.
11337 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11338 * @return {Number} The index of the passed Record. Returns -1 if not found.
11340 indexOf : function(record){
11341 return this.data.indexOf(record);
11345 * Get the index within the cache of the Record with the passed id.
11346 * @param {String} id The id of the Record to find.
11347 * @return {Number} The index of the Record. Returns -1 if not found.
11349 indexOfId : function(id){
11350 return this.data.indexOfKey(id);
11354 * Get the Record with the specified id.
11355 * @param {String} id The id of the Record to find.
11356 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11358 getById : function(id){
11359 return this.data.key(id);
11363 * Get the Record at the specified index.
11364 * @param {Number} index The index of the Record to find.
11365 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11367 getAt : function(index){
11368 return this.data.itemAt(index);
11372 * Returns a range of Records between specified indices.
11373 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11374 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11375 * @return {Roo.data.Record[]} An array of Records
11377 getRange : function(start, end){
11378 return this.data.getRange(start, end);
11382 storeOptions : function(o){
11383 o = Roo.apply({}, o);
11386 this.lastOptions = o;
11390 * Loads the Record cache from the configured Proxy using the configured Reader.
11392 * If using remote paging, then the first load call must specify the <em>start</em>
11393 * and <em>limit</em> properties in the options.params property to establish the initial
11394 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11396 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11397 * and this call will return before the new data has been loaded. Perform any post-processing
11398 * in a callback function, or in a "load" event handler.</strong>
11400 * @param {Object} options An object containing properties which control loading options:<ul>
11401 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11402 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11403 * passed the following arguments:<ul>
11404 * <li>r : Roo.data.Record[]</li>
11405 * <li>options: Options object from the load call</li>
11406 * <li>success: Boolean success indicator</li></ul></li>
11407 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11408 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11411 load : function(options){
11412 options = options || {};
11413 if(this.fireEvent("beforeload", this, options) !== false){
11414 this.storeOptions(options);
11415 var p = Roo.apply(options.params || {}, this.baseParams);
11416 // if meta was not loaded from remote source.. try requesting it.
11417 if (!this.reader.metaFromRemote) {
11418 p._requestMeta = 1;
11420 if(this.sortInfo && this.remoteSort){
11421 var pn = this.paramNames;
11422 p[pn["sort"]] = this.sortInfo.field;
11423 p[pn["dir"]] = this.sortInfo.direction;
11425 if (this.multiSort) {
11426 var pn = this.paramNames;
11427 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11430 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11435 * Reloads the Record cache from the configured Proxy using the configured Reader and
11436 * the options from the last load operation performed.
11437 * @param {Object} options (optional) An object containing properties which may override the options
11438 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11439 * the most recently used options are reused).
11441 reload : function(options){
11442 this.load(Roo.applyIf(options||{}, this.lastOptions));
11446 // Called as a callback by the Reader during a load operation.
11447 loadRecords : function(o, options, success){
11448 if(!o || success === false){
11449 if(success !== false){
11450 this.fireEvent("load", this, [], options, o);
11452 if(options.callback){
11453 options.callback.call(options.scope || this, [], options, false);
11457 // if data returned failure - throw an exception.
11458 if (o.success === false) {
11459 // show a message if no listener is registered.
11460 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11461 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11463 // loadmask wil be hooked into this..
11464 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11467 var r = o.records, t = o.totalRecords || r.length;
11469 this.fireEvent("beforeloadadd", this, r, options, o);
11471 if(!options || options.add !== true){
11472 if(this.pruneModifiedRecords){
11473 this.modified = [];
11475 for(var i = 0, len = r.length; i < len; i++){
11479 this.data = this.snapshot;
11480 delete this.snapshot;
11483 this.data.addAll(r);
11484 this.totalLength = t;
11486 this.fireEvent("datachanged", this);
11488 this.totalLength = Math.max(t, this.data.length+r.length);
11492 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11494 var e = new Roo.data.Record({});
11496 e.set(this.parent.displayField, this.parent.emptyTitle);
11497 e.set(this.parent.valueField, '');
11502 this.fireEvent("load", this, r, options, o);
11503 if(options.callback){
11504 options.callback.call(options.scope || this, r, options, true);
11510 * Loads data from a passed data block. A Reader which understands the format of the data
11511 * must have been configured in the constructor.
11512 * @param {Object} data The data block from which to read the Records. The format of the data expected
11513 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11514 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11516 loadData : function(o, append){
11517 var r = this.reader.readRecords(o);
11518 this.loadRecords(r, {add: append}, true);
11522 * Gets the number of cached records.
11524 * <em>If using paging, this may not be the total size of the dataset. If the data object
11525 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11526 * the data set size</em>
11528 getCount : function(){
11529 return this.data.length || 0;
11533 * Gets the total number of records in the dataset as returned by the server.
11535 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11536 * the dataset size</em>
11538 getTotalCount : function(){
11539 return this.totalLength || 0;
11543 * Returns the sort state of the Store as an object with two properties:
11545 field {String} The name of the field by which the Records are sorted
11546 direction {String} The sort order, "ASC" or "DESC"
11549 getSortState : function(){
11550 return this.sortInfo;
11554 applySort : function(){
11555 if(this.sortInfo && !this.remoteSort){
11556 var s = this.sortInfo, f = s.field;
11557 var st = this.fields.get(f).sortType;
11558 var fn = function(r1, r2){
11559 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11560 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11562 this.data.sort(s.direction, fn);
11563 if(this.snapshot && this.snapshot != this.data){
11564 this.snapshot.sort(s.direction, fn);
11570 * Sets the default sort column and order to be used by the next load operation.
11571 * @param {String} fieldName The name of the field to sort by.
11572 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11574 setDefaultSort : function(field, dir){
11575 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11579 * Sort the Records.
11580 * If remote sorting is used, the sort is performed on the server, and the cache is
11581 * reloaded. If local sorting is used, the cache is sorted internally.
11582 * @param {String} fieldName The name of the field to sort by.
11583 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11585 sort : function(fieldName, dir){
11586 var f = this.fields.get(fieldName);
11588 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11590 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11591 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11596 this.sortToggle[f.name] = dir;
11597 this.sortInfo = {field: f.name, direction: dir};
11598 if(!this.remoteSort){
11600 this.fireEvent("datachanged", this);
11602 this.load(this.lastOptions);
11607 * Calls the specified function for each of the Records in the cache.
11608 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11609 * Returning <em>false</em> aborts and exits the iteration.
11610 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11612 each : function(fn, scope){
11613 this.data.each(fn, scope);
11617 * Gets all records modified since the last commit. Modified records are persisted across load operations
11618 * (e.g., during paging).
11619 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11621 getModifiedRecords : function(){
11622 return this.modified;
11626 createFilterFn : function(property, value, anyMatch){
11627 if(!value.exec){ // not a regex
11628 value = String(value);
11629 if(value.length == 0){
11632 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11634 return function(r){
11635 return value.test(r.data[property]);
11640 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11641 * @param {String} property A field on your records
11642 * @param {Number} start The record index to start at (defaults to 0)
11643 * @param {Number} end The last record index to include (defaults to length - 1)
11644 * @return {Number} The sum
11646 sum : function(property, start, end){
11647 var rs = this.data.items, v = 0;
11648 start = start || 0;
11649 end = (end || end === 0) ? end : rs.length-1;
11651 for(var i = start; i <= end; i++){
11652 v += (rs[i].data[property] || 0);
11658 * Filter the records by a specified property.
11659 * @param {String} field A field on your records
11660 * @param {String/RegExp} value Either a string that the field
11661 * should start with or a RegExp to test against the field
11662 * @param {Boolean} anyMatch True to match any part not just the beginning
11664 filter : function(property, value, anyMatch){
11665 var fn = this.createFilterFn(property, value, anyMatch);
11666 return fn ? this.filterBy(fn) : this.clearFilter();
11670 * Filter by a function. The specified function will be called with each
11671 * record in this data source. If the function returns true the record is included,
11672 * otherwise it is filtered.
11673 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11674 * @param {Object} scope (optional) The scope of the function (defaults to this)
11676 filterBy : function(fn, scope){
11677 this.snapshot = this.snapshot || this.data;
11678 this.data = this.queryBy(fn, scope||this);
11679 this.fireEvent("datachanged", this);
11683 * Query the records by a specified property.
11684 * @param {String} field A field on your records
11685 * @param {String/RegExp} value Either a string that the field
11686 * should start with or a RegExp to test against the field
11687 * @param {Boolean} anyMatch True to match any part not just the beginning
11688 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11690 query : function(property, value, anyMatch){
11691 var fn = this.createFilterFn(property, value, anyMatch);
11692 return fn ? this.queryBy(fn) : this.data.clone();
11696 * Query by a function. The specified function will be called with each
11697 * record in this data source. If the function returns true the record is included
11699 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11700 * @param {Object} scope (optional) The scope of the function (defaults to this)
11701 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11703 queryBy : function(fn, scope){
11704 var data = this.snapshot || this.data;
11705 return data.filterBy(fn, scope||this);
11709 * Collects unique values for a particular dataIndex from this store.
11710 * @param {String} dataIndex The property to collect
11711 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11712 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11713 * @return {Array} An array of the unique values
11715 collect : function(dataIndex, allowNull, bypassFilter){
11716 var d = (bypassFilter === true && this.snapshot) ?
11717 this.snapshot.items : this.data.items;
11718 var v, sv, r = [], l = {};
11719 for(var i = 0, len = d.length; i < len; i++){
11720 v = d[i].data[dataIndex];
11722 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11731 * Revert to a view of the Record cache with no filtering applied.
11732 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11734 clearFilter : function(suppressEvent){
11735 if(this.snapshot && this.snapshot != this.data){
11736 this.data = this.snapshot;
11737 delete this.snapshot;
11738 if(suppressEvent !== true){
11739 this.fireEvent("datachanged", this);
11745 afterEdit : function(record){
11746 if(this.modified.indexOf(record) == -1){
11747 this.modified.push(record);
11749 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11753 afterReject : function(record){
11754 this.modified.remove(record);
11755 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11759 afterCommit : function(record){
11760 this.modified.remove(record);
11761 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11765 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11766 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11768 commitChanges : function(){
11769 var m = this.modified.slice(0);
11770 this.modified = [];
11771 for(var i = 0, len = m.length; i < len; i++){
11777 * Cancel outstanding changes on all changed records.
11779 rejectChanges : function(){
11780 var m = this.modified.slice(0);
11781 this.modified = [];
11782 for(var i = 0, len = m.length; i < len; i++){
11787 onMetaChange : function(meta, rtype, o){
11788 this.recordType = rtype;
11789 this.fields = rtype.prototype.fields;
11790 delete this.snapshot;
11791 this.sortInfo = meta.sortInfo || this.sortInfo;
11792 this.modified = [];
11793 this.fireEvent('metachange', this, this.reader.meta);
11796 moveIndex : function(data, type)
11798 var index = this.indexOf(data);
11800 var newIndex = index + type;
11804 this.insert(newIndex, data);
11809 * Ext JS Library 1.1.1
11810 * Copyright(c) 2006-2007, Ext JS, LLC.
11812 * Originally Released Under LGPL - original licence link has changed is not relivant.
11815 * <script type="text/javascript">
11819 * @class Roo.data.SimpleStore
11820 * @extends Roo.data.Store
11821 * Small helper class to make creating Stores from Array data easier.
11822 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11823 * @cfg {Array} fields An array of field definition objects, or field name strings.
11824 * @cfg {Array} data The multi-dimensional array of data
11826 * @param {Object} config
11828 Roo.data.SimpleStore = function(config){
11829 Roo.data.SimpleStore.superclass.constructor.call(this, {
11831 reader: new Roo.data.ArrayReader({
11834 Roo.data.Record.create(config.fields)
11836 proxy : new Roo.data.MemoryProxy(config.data)
11840 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11842 * Ext JS Library 1.1.1
11843 * Copyright(c) 2006-2007, Ext JS, LLC.
11845 * Originally Released Under LGPL - original licence link has changed is not relivant.
11848 * <script type="text/javascript">
11853 * @extends Roo.data.Store
11854 * @class Roo.data.JsonStore
11855 * Small helper class to make creating Stores for JSON data easier. <br/>
11857 var store = new Roo.data.JsonStore({
11858 url: 'get-images.php',
11860 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11863 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11864 * JsonReader and HttpProxy (unless inline data is provided).</b>
11865 * @cfg {Array} fields An array of field definition objects, or field name strings.
11867 * @param {Object} config
11869 Roo.data.JsonStore = function(c){
11870 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11871 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11872 reader: new Roo.data.JsonReader(c, c.fields)
11875 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11877 * Ext JS Library 1.1.1
11878 * Copyright(c) 2006-2007, Ext JS, LLC.
11880 * Originally Released Under LGPL - original licence link has changed is not relivant.
11883 * <script type="text/javascript">
11887 Roo.data.Field = function(config){
11888 if(typeof config == "string"){
11889 config = {name: config};
11891 Roo.apply(this, config);
11894 this.type = "auto";
11897 var st = Roo.data.SortTypes;
11898 // named sortTypes are supported, here we look them up
11899 if(typeof this.sortType == "string"){
11900 this.sortType = st[this.sortType];
11903 // set default sortType for strings and dates
11904 if(!this.sortType){
11907 this.sortType = st.asUCString;
11910 this.sortType = st.asDate;
11913 this.sortType = st.none;
11918 var stripRe = /[\$,%]/g;
11920 // prebuilt conversion function for this field, instead of
11921 // switching every time we're reading a value
11923 var cv, dateFormat = this.dateFormat;
11928 cv = function(v){ return v; };
11931 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11935 return v !== undefined && v !== null && v !== '' ?
11936 parseInt(String(v).replace(stripRe, ""), 10) : '';
11941 return v !== undefined && v !== null && v !== '' ?
11942 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11947 cv = function(v){ return v === true || v === "true" || v == 1; };
11954 if(v instanceof Date){
11958 if(dateFormat == "timestamp"){
11959 return new Date(v*1000);
11961 return Date.parseDate(v, dateFormat);
11963 var parsed = Date.parse(v);
11964 return parsed ? new Date(parsed) : null;
11973 Roo.data.Field.prototype = {
11981 * Ext JS Library 1.1.1
11982 * Copyright(c) 2006-2007, Ext JS, LLC.
11984 * Originally Released Under LGPL - original licence link has changed is not relivant.
11987 * <script type="text/javascript">
11990 // Base class for reading structured data from a data source. This class is intended to be
11991 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11994 * @class Roo.data.DataReader
11995 * Base class for reading structured data from a data source. This class is intended to be
11996 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11999 Roo.data.DataReader = function(meta, recordType){
12003 this.recordType = recordType instanceof Array ?
12004 Roo.data.Record.create(recordType) : recordType;
12007 Roo.data.DataReader.prototype = {
12009 * Create an empty record
12010 * @param {Object} data (optional) - overlay some values
12011 * @return {Roo.data.Record} record created.
12013 newRow : function(d) {
12015 this.recordType.prototype.fields.each(function(c) {
12017 case 'int' : da[c.name] = 0; break;
12018 case 'date' : da[c.name] = new Date(); break;
12019 case 'float' : da[c.name] = 0.0; break;
12020 case 'boolean' : da[c.name] = false; break;
12021 default : da[c.name] = ""; break;
12025 return new this.recordType(Roo.apply(da, d));
12030 * Ext JS Library 1.1.1
12031 * Copyright(c) 2006-2007, Ext JS, LLC.
12033 * Originally Released Under LGPL - original licence link has changed is not relivant.
12036 * <script type="text/javascript">
12040 * @class Roo.data.DataProxy
12041 * @extends Roo.data.Observable
12042 * This class is an abstract base class for implementations which provide retrieval of
12043 * unformatted data objects.<br>
12045 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12046 * (of the appropriate type which knows how to parse the data object) to provide a block of
12047 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12049 * Custom implementations must implement the load method as described in
12050 * {@link Roo.data.HttpProxy#load}.
12052 Roo.data.DataProxy = function(){
12055 * @event beforeload
12056 * Fires before a network request is made to retrieve a data object.
12057 * @param {Object} This DataProxy object.
12058 * @param {Object} params The params parameter to the load function.
12063 * Fires before the load method's callback is called.
12064 * @param {Object} This DataProxy object.
12065 * @param {Object} o The data object.
12066 * @param {Object} arg The callback argument object passed to the load function.
12070 * @event loadexception
12071 * Fires if an Exception occurs during data retrieval.
12072 * @param {Object} This DataProxy object.
12073 * @param {Object} o The data object.
12074 * @param {Object} arg The callback argument object passed to the load function.
12075 * @param {Object} e The Exception.
12077 loadexception : true
12079 Roo.data.DataProxy.superclass.constructor.call(this);
12082 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12085 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12089 * Ext JS Library 1.1.1
12090 * Copyright(c) 2006-2007, Ext JS, LLC.
12092 * Originally Released Under LGPL - original licence link has changed is not relivant.
12095 * <script type="text/javascript">
12098 * @class Roo.data.MemoryProxy
12099 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12100 * to the Reader when its load method is called.
12102 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12104 Roo.data.MemoryProxy = function(data){
12108 Roo.data.MemoryProxy.superclass.constructor.call(this);
12112 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12115 * Load data from the requested source (in this case an in-memory
12116 * data object passed to the constructor), read the data object into
12117 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12118 * process that block using the passed callback.
12119 * @param {Object} params This parameter is not used by the MemoryProxy class.
12120 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12121 * object into a block of Roo.data.Records.
12122 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12123 * The function must be passed <ul>
12124 * <li>The Record block object</li>
12125 * <li>The "arg" argument from the load function</li>
12126 * <li>A boolean success indicator</li>
12128 * @param {Object} scope The scope in which to call the callback
12129 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12131 load : function(params, reader, callback, scope, arg){
12132 params = params || {};
12135 result = reader.readRecords(this.data);
12137 this.fireEvent("loadexception", this, arg, null, e);
12138 callback.call(scope, null, arg, false);
12141 callback.call(scope, result, arg, true);
12145 update : function(params, records){
12150 * Ext JS Library 1.1.1
12151 * Copyright(c) 2006-2007, Ext JS, LLC.
12153 * Originally Released Under LGPL - original licence link has changed is not relivant.
12156 * <script type="text/javascript">
12159 * @class Roo.data.HttpProxy
12160 * @extends Roo.data.DataProxy
12161 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12162 * configured to reference a certain URL.<br><br>
12164 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12165 * from which the running page was served.<br><br>
12167 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12169 * Be aware that to enable the browser to parse an XML document, the server must set
12170 * the Content-Type header in the HTTP response to "text/xml".
12172 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12173 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12174 * will be used to make the request.
12176 Roo.data.HttpProxy = function(conn){
12177 Roo.data.HttpProxy.superclass.constructor.call(this);
12178 // is conn a conn config or a real conn?
12180 this.useAjax = !conn || !conn.events;
12184 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12185 // thse are take from connection...
12188 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12191 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12192 * extra parameters to each request made by this object. (defaults to undefined)
12195 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12196 * to each request made by this object. (defaults to undefined)
12199 * @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)
12202 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12205 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12211 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12215 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12216 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12217 * a finer-grained basis than the DataProxy events.
12219 getConnection : function(){
12220 return this.useAjax ? Roo.Ajax : this.conn;
12224 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12225 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12226 * process that block using the passed callback.
12227 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12228 * for the request to the remote server.
12229 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12230 * object into a block of Roo.data.Records.
12231 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12232 * The function must be passed <ul>
12233 * <li>The Record block object</li>
12234 * <li>The "arg" argument from the load function</li>
12235 * <li>A boolean success indicator</li>
12237 * @param {Object} scope The scope in which to call the callback
12238 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12240 load : function(params, reader, callback, scope, arg){
12241 if(this.fireEvent("beforeload", this, params) !== false){
12243 params : params || {},
12245 callback : callback,
12250 callback : this.loadResponse,
12254 Roo.applyIf(o, this.conn);
12255 if(this.activeRequest){
12256 Roo.Ajax.abort(this.activeRequest);
12258 this.activeRequest = Roo.Ajax.request(o);
12260 this.conn.request(o);
12263 callback.call(scope||this, null, arg, false);
12268 loadResponse : function(o, success, response){
12269 delete this.activeRequest;
12271 this.fireEvent("loadexception", this, o, response);
12272 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12277 result = o.reader.read(response);
12279 this.fireEvent("loadexception", this, o, response, e);
12280 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12284 this.fireEvent("load", this, o, o.request.arg);
12285 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12289 update : function(dataSet){
12294 updateResponse : function(dataSet){
12299 * Ext JS Library 1.1.1
12300 * Copyright(c) 2006-2007, Ext JS, LLC.
12302 * Originally Released Under LGPL - original licence link has changed is not relivant.
12305 * <script type="text/javascript">
12309 * @class Roo.data.ScriptTagProxy
12310 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12311 * other than the originating domain of the running page.<br><br>
12313 * <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
12314 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12316 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12317 * source code that is used as the source inside a <script> tag.<br><br>
12319 * In order for the browser to process the returned data, the server must wrap the data object
12320 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12321 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12322 * depending on whether the callback name was passed:
12325 boolean scriptTag = false;
12326 String cb = request.getParameter("callback");
12329 response.setContentType("text/javascript");
12331 response.setContentType("application/x-json");
12333 Writer out = response.getWriter();
12335 out.write(cb + "(");
12337 out.print(dataBlock.toJsonString());
12344 * @param {Object} config A configuration object.
12346 Roo.data.ScriptTagProxy = function(config){
12347 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12348 Roo.apply(this, config);
12349 this.head = document.getElementsByTagName("head")[0];
12352 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12354 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12356 * @cfg {String} url The URL from which to request the data object.
12359 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12363 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12364 * the server the name of the callback function set up by the load call to process the returned data object.
12365 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12366 * javascript output which calls this named function passing the data object as its only parameter.
12368 callbackParam : "callback",
12370 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12371 * name to the request.
12376 * Load data from the configured URL, read the data object into
12377 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12378 * process that block using the passed callback.
12379 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12380 * for the request to the remote server.
12381 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12382 * object into a block of Roo.data.Records.
12383 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12384 * The function must be passed <ul>
12385 * <li>The Record block object</li>
12386 * <li>The "arg" argument from the load function</li>
12387 * <li>A boolean success indicator</li>
12389 * @param {Object} scope The scope in which to call the callback
12390 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12392 load : function(params, reader, callback, scope, arg){
12393 if(this.fireEvent("beforeload", this, params) !== false){
12395 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12397 var url = this.url;
12398 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12400 url += "&_dc=" + (new Date().getTime());
12402 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12405 cb : "stcCallback"+transId,
12406 scriptId : "stcScript"+transId,
12410 callback : callback,
12416 window[trans.cb] = function(o){
12417 conn.handleResponse(o, trans);
12420 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12422 if(this.autoAbort !== false){
12426 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12428 var script = document.createElement("script");
12429 script.setAttribute("src", url);
12430 script.setAttribute("type", "text/javascript");
12431 script.setAttribute("id", trans.scriptId);
12432 this.head.appendChild(script);
12434 this.trans = trans;
12436 callback.call(scope||this, null, arg, false);
12441 isLoading : function(){
12442 return this.trans ? true : false;
12446 * Abort the current server request.
12448 abort : function(){
12449 if(this.isLoading()){
12450 this.destroyTrans(this.trans);
12455 destroyTrans : function(trans, isLoaded){
12456 this.head.removeChild(document.getElementById(trans.scriptId));
12457 clearTimeout(trans.timeoutId);
12459 window[trans.cb] = undefined;
12461 delete window[trans.cb];
12464 // if hasn't been loaded, wait for load to remove it to prevent script error
12465 window[trans.cb] = function(){
12466 window[trans.cb] = undefined;
12468 delete window[trans.cb];
12475 handleResponse : function(o, trans){
12476 this.trans = false;
12477 this.destroyTrans(trans, true);
12480 result = trans.reader.readRecords(o);
12482 this.fireEvent("loadexception", this, o, trans.arg, e);
12483 trans.callback.call(trans.scope||window, null, trans.arg, false);
12486 this.fireEvent("load", this, o, trans.arg);
12487 trans.callback.call(trans.scope||window, result, trans.arg, true);
12491 handleFailure : function(trans){
12492 this.trans = false;
12493 this.destroyTrans(trans, false);
12494 this.fireEvent("loadexception", this, null, trans.arg);
12495 trans.callback.call(trans.scope||window, null, trans.arg, false);
12499 * Ext JS Library 1.1.1
12500 * Copyright(c) 2006-2007, Ext JS, LLC.
12502 * Originally Released Under LGPL - original licence link has changed is not relivant.
12505 * <script type="text/javascript">
12509 * @class Roo.data.JsonReader
12510 * @extends Roo.data.DataReader
12511 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12512 * based on mappings in a provided Roo.data.Record constructor.
12514 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12515 * in the reply previously.
12520 var RecordDef = Roo.data.Record.create([
12521 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12522 {name: 'occupation'} // This field will use "occupation" as the mapping.
12524 var myReader = new Roo.data.JsonReader({
12525 totalProperty: "results", // The property which contains the total dataset size (optional)
12526 root: "rows", // The property which contains an Array of row objects
12527 id: "id" // The property within each row object that provides an ID for the record (optional)
12531 * This would consume a JSON file like this:
12533 { 'results': 2, 'rows': [
12534 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12535 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12538 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12539 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12540 * paged from the remote server.
12541 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12542 * @cfg {String} root name of the property which contains the Array of row objects.
12543 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12544 * @cfg {Array} fields Array of field definition objects
12546 * Create a new JsonReader
12547 * @param {Object} meta Metadata configuration options
12548 * @param {Object} recordType Either an Array of field definition objects,
12549 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12551 Roo.data.JsonReader = function(meta, recordType){
12554 // set some defaults:
12555 Roo.applyIf(meta, {
12556 totalProperty: 'total',
12557 successProperty : 'success',
12562 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12564 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12567 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12568 * Used by Store query builder to append _requestMeta to params.
12571 metaFromRemote : false,
12573 * This method is only used by a DataProxy which has retrieved data from a remote server.
12574 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12575 * @return {Object} data A data block which is used by an Roo.data.Store object as
12576 * a cache of Roo.data.Records.
12578 read : function(response){
12579 var json = response.responseText;
12581 var o = /* eval:var:o */ eval("("+json+")");
12583 throw {message: "JsonReader.read: Json object not found"};
12589 this.metaFromRemote = true;
12590 this.meta = o.metaData;
12591 this.recordType = Roo.data.Record.create(o.metaData.fields);
12592 this.onMetaChange(this.meta, this.recordType, o);
12594 return this.readRecords(o);
12597 // private function a store will implement
12598 onMetaChange : function(meta, recordType, o){
12605 simpleAccess: function(obj, subsc) {
12612 getJsonAccessor: function(){
12614 return function(expr) {
12616 return(re.test(expr))
12617 ? new Function("obj", "return obj." + expr)
12622 return Roo.emptyFn;
12627 * Create a data block containing Roo.data.Records from an XML document.
12628 * @param {Object} o An object which contains an Array of row objects in the property specified
12629 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12630 * which contains the total size of the dataset.
12631 * @return {Object} data A data block which is used by an Roo.data.Store object as
12632 * a cache of Roo.data.Records.
12634 readRecords : function(o){
12636 * After any data loads, the raw JSON data is available for further custom processing.
12640 var s = this.meta, Record = this.recordType,
12641 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12643 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12645 if(s.totalProperty) {
12646 this.getTotal = this.getJsonAccessor(s.totalProperty);
12648 if(s.successProperty) {
12649 this.getSuccess = this.getJsonAccessor(s.successProperty);
12651 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12653 var g = this.getJsonAccessor(s.id);
12654 this.getId = function(rec) {
12656 return (r === undefined || r === "") ? null : r;
12659 this.getId = function(){return null;};
12662 for(var jj = 0; jj < fl; jj++){
12664 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12665 this.ef[jj] = this.getJsonAccessor(map);
12669 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12670 if(s.totalProperty){
12671 var vt = parseInt(this.getTotal(o), 10);
12676 if(s.successProperty){
12677 var vs = this.getSuccess(o);
12678 if(vs === false || vs === 'false'){
12683 for(var i = 0; i < c; i++){
12686 var id = this.getId(n);
12687 for(var j = 0; j < fl; j++){
12689 var v = this.ef[j](n);
12691 Roo.log('missing convert for ' + f.name);
12695 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12697 var record = new Record(values, id);
12699 records[i] = record;
12705 totalRecords : totalRecords
12710 * Ext JS Library 1.1.1
12711 * Copyright(c) 2006-2007, Ext JS, LLC.
12713 * Originally Released Under LGPL - original licence link has changed is not relivant.
12716 * <script type="text/javascript">
12720 * @class Roo.data.ArrayReader
12721 * @extends Roo.data.DataReader
12722 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12723 * Each element of that Array represents a row of data fields. The
12724 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12725 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12729 var RecordDef = Roo.data.Record.create([
12730 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12731 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12733 var myReader = new Roo.data.ArrayReader({
12734 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12738 * This would consume an Array like this:
12740 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12742 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12744 * Create a new JsonReader
12745 * @param {Object} meta Metadata configuration options.
12746 * @param {Object} recordType Either an Array of field definition objects
12747 * as specified to {@link Roo.data.Record#create},
12748 * or an {@link Roo.data.Record} object
12749 * created using {@link Roo.data.Record#create}.
12751 Roo.data.ArrayReader = function(meta, recordType){
12752 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12755 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12757 * Create a data block containing Roo.data.Records from an XML document.
12758 * @param {Object} o An Array of row objects which represents the dataset.
12759 * @return {Object} data A data block which is used by an Roo.data.Store object as
12760 * a cache of Roo.data.Records.
12762 readRecords : function(o){
12763 var sid = this.meta ? this.meta.id : null;
12764 var recordType = this.recordType, fields = recordType.prototype.fields;
12767 for(var i = 0; i < root.length; i++){
12770 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12771 for(var j = 0, jlen = fields.length; j < jlen; j++){
12772 var f = fields.items[j];
12773 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12774 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12776 values[f.name] = v;
12778 var record = new recordType(values, id);
12780 records[records.length] = record;
12784 totalRecords : records.length
12793 * @class Roo.bootstrap.ComboBox
12794 * @extends Roo.bootstrap.TriggerField
12795 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12796 * @cfg {Boolean} append (true|false) default false
12797 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12798 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12799 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12800 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12801 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12802 * @cfg {Boolean} animate default true
12803 * @cfg {Boolean} emptyResultText only for touch device
12804 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12805 * @cfg {String} emptyTitle default ''
12807 * Create a new ComboBox.
12808 * @param {Object} config Configuration options
12810 Roo.bootstrap.ComboBox = function(config){
12811 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12815 * Fires when the dropdown list is expanded
12816 * @param {Roo.bootstrap.ComboBox} combo This combo box
12821 * Fires when the dropdown list is collapsed
12822 * @param {Roo.bootstrap.ComboBox} combo This combo box
12826 * @event beforeselect
12827 * Fires before a list item is selected. Return false to cancel the selection.
12828 * @param {Roo.bootstrap.ComboBox} combo This combo box
12829 * @param {Roo.data.Record} record The data record returned from the underlying store
12830 * @param {Number} index The index of the selected item in the dropdown list
12832 'beforeselect' : true,
12835 * Fires when a list item is selected
12836 * @param {Roo.bootstrap.ComboBox} combo This combo box
12837 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12838 * @param {Number} index The index of the selected item in the dropdown list
12842 * @event beforequery
12843 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12844 * The event object passed has these properties:
12845 * @param {Roo.bootstrap.ComboBox} combo This combo box
12846 * @param {String} query The query
12847 * @param {Boolean} forceAll true to force "all" query
12848 * @param {Boolean} cancel true to cancel the query
12849 * @param {Object} e The query event object
12851 'beforequery': true,
12854 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12855 * @param {Roo.bootstrap.ComboBox} combo This combo box
12860 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12861 * @param {Roo.bootstrap.ComboBox} combo This combo box
12862 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12867 * Fires when the remove value from the combobox array
12868 * @param {Roo.bootstrap.ComboBox} combo This combo box
12872 * @event afterremove
12873 * Fires when the remove value from the combobox array
12874 * @param {Roo.bootstrap.ComboBox} combo This combo box
12876 'afterremove' : true,
12878 * @event specialfilter
12879 * Fires when specialfilter
12880 * @param {Roo.bootstrap.ComboBox} combo This combo box
12882 'specialfilter' : true,
12885 * Fires when tick the element
12886 * @param {Roo.bootstrap.ComboBox} combo This combo box
12890 * @event touchviewdisplay
12891 * Fires when touch view require special display (default is using displayField)
12892 * @param {Roo.bootstrap.ComboBox} combo This combo box
12893 * @param {Object} cfg set html .
12895 'touchviewdisplay' : true
12900 this.tickItems = [];
12902 this.selectedIndex = -1;
12903 if(this.mode == 'local'){
12904 if(config.queryDelay === undefined){
12905 this.queryDelay = 10;
12907 if(config.minChars === undefined){
12913 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12916 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12917 * rendering into an Roo.Editor, defaults to false)
12920 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12921 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12924 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12927 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12928 * the dropdown list (defaults to undefined, with no header element)
12932 * @cfg {String/Roo.Template} tpl The template to use to render the output
12936 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12938 listWidth: undefined,
12940 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12941 * mode = 'remote' or 'text' if mode = 'local')
12943 displayField: undefined,
12946 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12947 * mode = 'remote' or 'value' if mode = 'local').
12948 * Note: use of a valueField requires the user make a selection
12949 * in order for a value to be mapped.
12951 valueField: undefined,
12953 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12958 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12959 * field's data value (defaults to the underlying DOM element's name)
12961 hiddenName: undefined,
12963 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12967 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12969 selectedClass: 'active',
12972 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12976 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12977 * anchor positions (defaults to 'tl-bl')
12979 listAlign: 'tl-bl?',
12981 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12985 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12986 * query specified by the allQuery config option (defaults to 'query')
12988 triggerAction: 'query',
12990 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12991 * (defaults to 4, does not apply if editable = false)
12995 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12996 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13000 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13001 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13005 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13006 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13010 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13011 * when editable = true (defaults to false)
13013 selectOnFocus:false,
13015 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13017 queryParam: 'query',
13019 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13020 * when mode = 'remote' (defaults to 'Loading...')
13022 loadingText: 'Loading...',
13024 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13028 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13032 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13033 * traditional select (defaults to true)
13037 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13041 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13045 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13046 * listWidth has a higher value)
13050 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13051 * allow the user to set arbitrary text into the field (defaults to false)
13053 forceSelection:false,
13055 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13056 * if typeAhead = true (defaults to 250)
13058 typeAheadDelay : 250,
13060 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13061 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13063 valueNotFoundText : undefined,
13065 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13067 blockFocus : false,
13070 * @cfg {Boolean} disableClear Disable showing of clear button.
13072 disableClear : false,
13074 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13076 alwaysQuery : false,
13079 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13084 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13086 invalidClass : "has-warning",
13089 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13091 validClass : "has-success",
13094 * @cfg {Boolean} specialFilter (true|false) special filter default false
13096 specialFilter : false,
13099 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13101 mobileTouchView : true,
13104 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13106 useNativeIOS : false,
13109 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13111 mobile_restrict_height : false,
13113 ios_options : false,
13125 btnPosition : 'right',
13126 triggerList : true,
13127 showToggleBtn : true,
13129 emptyResultText: 'Empty',
13130 triggerText : 'Select',
13133 // element that contains real text value.. (when hidden is used..)
13135 getAutoCreate : function()
13140 * Render classic select for iso
13143 if(Roo.isIOS && this.useNativeIOS){
13144 cfg = this.getAutoCreateNativeIOS();
13152 if(Roo.isTouch && this.mobileTouchView){
13153 cfg = this.getAutoCreateTouchView();
13160 if(!this.tickable){
13161 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13166 * ComboBox with tickable selections
13169 var align = this.labelAlign || this.parentLabelAlign();
13172 cls : 'form-group roo-combobox-tickable' //input-group
13175 var btn_text_select = '';
13176 var btn_text_done = '';
13177 var btn_text_cancel = '';
13179 if (this.btn_text_show) {
13180 btn_text_select = 'Select';
13181 btn_text_done = 'Done';
13182 btn_text_cancel = 'Cancel';
13187 cls : 'tickable-buttons',
13192 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13193 //html : this.triggerText
13194 html: btn_text_select
13200 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13202 html: btn_text_done
13208 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13210 html: btn_text_cancel
13216 buttons.cn.unshift({
13218 cls: 'roo-select2-search-field-input'
13224 Roo.each(buttons.cn, function(c){
13226 c.cls += ' btn-' + _this.size;
13229 if (_this.disabled) {
13240 cls: 'form-hidden-field'
13244 cls: 'roo-select2-choices',
13248 cls: 'roo-select2-search-field',
13259 cls: 'roo-select2-container input-group roo-select2-container-multi',
13264 // cls: 'typeahead typeahead-long dropdown-menu',
13265 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13270 if(this.hasFeedback && !this.allowBlank){
13274 cls: 'glyphicon form-control-feedback'
13277 combobox.cn.push(feedback);
13281 if (align ==='left' && this.fieldLabel.length) {
13283 cfg.cls += ' roo-form-group-label-left';
13288 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13289 tooltip : 'This field is required'
13294 cls : 'control-label',
13295 html : this.fieldLabel
13307 var labelCfg = cfg.cn[1];
13308 var contentCfg = cfg.cn[2];
13311 if(this.indicatorpos == 'right'){
13317 cls : 'control-label',
13321 html : this.fieldLabel
13325 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13326 tooltip : 'This field is required'
13341 labelCfg = cfg.cn[0];
13342 contentCfg = cfg.cn[1];
13346 if(this.labelWidth > 12){
13347 labelCfg.style = "width: " + this.labelWidth + 'px';
13350 if(this.labelWidth < 13 && this.labelmd == 0){
13351 this.labelmd = this.labelWidth;
13354 if(this.labellg > 0){
13355 labelCfg.cls += ' col-lg-' + this.labellg;
13356 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13359 if(this.labelmd > 0){
13360 labelCfg.cls += ' col-md-' + this.labelmd;
13361 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13364 if(this.labelsm > 0){
13365 labelCfg.cls += ' col-sm-' + this.labelsm;
13366 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13369 if(this.labelxs > 0){
13370 labelCfg.cls += ' col-xs-' + this.labelxs;
13371 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13375 } else if ( this.fieldLabel.length) {
13376 // Roo.log(" label");
13380 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13381 tooltip : 'This field is required'
13385 //cls : 'input-group-addon',
13386 html : this.fieldLabel
13391 if(this.indicatorpos == 'right'){
13395 //cls : 'input-group-addon',
13396 html : this.fieldLabel
13400 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13401 tooltip : 'This field is required'
13410 // Roo.log(" no label && no align");
13417 ['xs','sm','md','lg'].map(function(size){
13418 if (settings[size]) {
13419 cfg.cls += ' col-' + size + '-' + settings[size];
13427 _initEventsCalled : false,
13430 initEvents: function()
13432 if (this._initEventsCalled) { // as we call render... prevent looping...
13435 this._initEventsCalled = true;
13438 throw "can not find store for combo";
13441 this.indicator = this.indicatorEl();
13443 this.store = Roo.factory(this.store, Roo.data);
13444 this.store.parent = this;
13446 // if we are building from html. then this element is so complex, that we can not really
13447 // use the rendered HTML.
13448 // so we have to trash and replace the previous code.
13449 if (Roo.XComponent.build_from_html) {
13450 // remove this element....
13451 var e = this.el.dom, k=0;
13452 while (e ) { e = e.previousSibling; ++k;}
13457 this.rendered = false;
13459 this.render(this.parent().getChildContainer(true), k);
13462 if(Roo.isIOS && this.useNativeIOS){
13463 this.initIOSView();
13471 if(Roo.isTouch && this.mobileTouchView){
13472 this.initTouchView();
13477 this.initTickableEvents();
13481 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13483 if(this.hiddenName){
13485 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13487 this.hiddenField.dom.value =
13488 this.hiddenValue !== undefined ? this.hiddenValue :
13489 this.value !== undefined ? this.value : '';
13491 // prevent input submission
13492 this.el.dom.removeAttribute('name');
13493 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13498 // this.el.dom.setAttribute('autocomplete', 'off');
13501 var cls = 'x-combo-list';
13503 //this.list = new Roo.Layer({
13504 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13510 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13511 _this.list.setWidth(lw);
13514 this.list.on('mouseover', this.onViewOver, this);
13515 this.list.on('mousemove', this.onViewMove, this);
13516 this.list.on('scroll', this.onViewScroll, this);
13519 this.list.swallowEvent('mousewheel');
13520 this.assetHeight = 0;
13523 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13524 this.assetHeight += this.header.getHeight();
13527 this.innerList = this.list.createChild({cls:cls+'-inner'});
13528 this.innerList.on('mouseover', this.onViewOver, this);
13529 this.innerList.on('mousemove', this.onViewMove, this);
13530 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13532 if(this.allowBlank && !this.pageSize && !this.disableClear){
13533 this.footer = this.list.createChild({cls:cls+'-ft'});
13534 this.pageTb = new Roo.Toolbar(this.footer);
13538 this.footer = this.list.createChild({cls:cls+'-ft'});
13539 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13540 {pageSize: this.pageSize});
13544 if (this.pageTb && this.allowBlank && !this.disableClear) {
13546 this.pageTb.add(new Roo.Toolbar.Fill(), {
13547 cls: 'x-btn-icon x-btn-clear',
13549 handler: function()
13552 _this.clearValue();
13553 _this.onSelect(false, -1);
13558 this.assetHeight += this.footer.getHeight();
13563 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13566 this.view = new Roo.View(this.list, this.tpl, {
13567 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13569 //this.view.wrapEl.setDisplayed(false);
13570 this.view.on('click', this.onViewClick, this);
13573 this.store.on('beforeload', this.onBeforeLoad, this);
13574 this.store.on('load', this.onLoad, this);
13575 this.store.on('loadexception', this.onLoadException, this);
13577 if(this.resizable){
13578 this.resizer = new Roo.Resizable(this.list, {
13579 pinned:true, handles:'se'
13581 this.resizer.on('resize', function(r, w, h){
13582 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13583 this.listWidth = w;
13584 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13585 this.restrictHeight();
13587 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13590 if(!this.editable){
13591 this.editable = true;
13592 this.setEditable(false);
13597 if (typeof(this.events.add.listeners) != 'undefined') {
13599 this.addicon = this.wrap.createChild(
13600 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13602 this.addicon.on('click', function(e) {
13603 this.fireEvent('add', this);
13606 if (typeof(this.events.edit.listeners) != 'undefined') {
13608 this.editicon = this.wrap.createChild(
13609 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13610 if (this.addicon) {
13611 this.editicon.setStyle('margin-left', '40px');
13613 this.editicon.on('click', function(e) {
13615 // we fire even if inothing is selected..
13616 this.fireEvent('edit', this, this.lastData );
13622 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13623 "up" : function(e){
13624 this.inKeyMode = true;
13628 "down" : function(e){
13629 if(!this.isExpanded()){
13630 this.onTriggerClick();
13632 this.inKeyMode = true;
13637 "enter" : function(e){
13638 // this.onViewClick();
13642 if(this.fireEvent("specialkey", this, e)){
13643 this.onViewClick(false);
13649 "esc" : function(e){
13653 "tab" : function(e){
13656 if(this.fireEvent("specialkey", this, e)){
13657 this.onViewClick(false);
13665 doRelay : function(foo, bar, hname){
13666 if(hname == 'down' || this.scope.isExpanded()){
13667 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13676 this.queryDelay = Math.max(this.queryDelay || 10,
13677 this.mode == 'local' ? 10 : 250);
13680 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13682 if(this.typeAhead){
13683 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13685 if(this.editable !== false){
13686 this.inputEl().on("keyup", this.onKeyUp, this);
13688 if(this.forceSelection){
13689 this.inputEl().on('blur', this.doForce, this);
13693 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13694 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13698 initTickableEvents: function()
13702 if(this.hiddenName){
13704 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13706 this.hiddenField.dom.value =
13707 this.hiddenValue !== undefined ? this.hiddenValue :
13708 this.value !== undefined ? this.value : '';
13710 // prevent input submission
13711 this.el.dom.removeAttribute('name');
13712 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13717 // this.list = this.el.select('ul.dropdown-menu',true).first();
13719 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13720 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13721 if(this.triggerList){
13722 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13725 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13726 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13728 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13729 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13731 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13732 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13734 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13735 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13736 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13739 this.cancelBtn.hide();
13744 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13745 _this.list.setWidth(lw);
13748 this.list.on('mouseover', this.onViewOver, this);
13749 this.list.on('mousemove', this.onViewMove, this);
13751 this.list.on('scroll', this.onViewScroll, this);
13754 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13755 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13758 this.view = new Roo.View(this.list, this.tpl, {
13763 selectedClass: this.selectedClass
13766 //this.view.wrapEl.setDisplayed(false);
13767 this.view.on('click', this.onViewClick, this);
13771 this.store.on('beforeload', this.onBeforeLoad, this);
13772 this.store.on('load', this.onLoad, this);
13773 this.store.on('loadexception', this.onLoadException, this);
13776 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13777 "up" : function(e){
13778 this.inKeyMode = true;
13782 "down" : function(e){
13783 this.inKeyMode = true;
13787 "enter" : function(e){
13788 if(this.fireEvent("specialkey", this, e)){
13789 this.onViewClick(false);
13795 "esc" : function(e){
13796 this.onTickableFooterButtonClick(e, false, false);
13799 "tab" : function(e){
13800 this.fireEvent("specialkey", this, e);
13802 this.onTickableFooterButtonClick(e, false, false);
13809 doRelay : function(e, fn, key){
13810 if(this.scope.isExpanded()){
13811 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13820 this.queryDelay = Math.max(this.queryDelay || 10,
13821 this.mode == 'local' ? 10 : 250);
13824 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13826 if(this.typeAhead){
13827 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13830 if(this.editable !== false){
13831 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13834 this.indicator = this.indicatorEl();
13836 if(this.indicator){
13837 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13838 this.indicator.hide();
13843 onDestroy : function(){
13845 this.view.setStore(null);
13846 this.view.el.removeAllListeners();
13847 this.view.el.remove();
13848 this.view.purgeListeners();
13851 this.list.dom.innerHTML = '';
13855 this.store.un('beforeload', this.onBeforeLoad, this);
13856 this.store.un('load', this.onLoad, this);
13857 this.store.un('loadexception', this.onLoadException, this);
13859 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13863 fireKey : function(e){
13864 if(e.isNavKeyPress() && !this.list.isVisible()){
13865 this.fireEvent("specialkey", this, e);
13870 onResize: function(w, h){
13871 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13873 // if(typeof w != 'number'){
13874 // // we do not handle it!?!?
13877 // var tw = this.trigger.getWidth();
13878 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13879 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13881 // this.inputEl().setWidth( this.adjustWidth('input', x));
13883 // //this.trigger.setStyle('left', x+'px');
13885 // if(this.list && this.listWidth === undefined){
13886 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13887 // this.list.setWidth(lw);
13888 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13896 * Allow or prevent the user from directly editing the field text. If false is passed,
13897 * the user will only be able to select from the items defined in the dropdown list. This method
13898 * is the runtime equivalent of setting the 'editable' config option at config time.
13899 * @param {Boolean} value True to allow the user to directly edit the field text
13901 setEditable : function(value){
13902 if(value == this.editable){
13905 this.editable = value;
13907 this.inputEl().dom.setAttribute('readOnly', true);
13908 this.inputEl().on('mousedown', this.onTriggerClick, this);
13909 this.inputEl().addClass('x-combo-noedit');
13911 this.inputEl().dom.setAttribute('readOnly', false);
13912 this.inputEl().un('mousedown', this.onTriggerClick, this);
13913 this.inputEl().removeClass('x-combo-noedit');
13919 onBeforeLoad : function(combo,opts){
13920 if(!this.hasFocus){
13924 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13926 this.restrictHeight();
13927 this.selectedIndex = -1;
13931 onLoad : function(){
13933 this.hasQuery = false;
13935 if(!this.hasFocus){
13939 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13940 this.loading.hide();
13943 if(this.store.getCount() > 0){
13946 this.restrictHeight();
13947 if(this.lastQuery == this.allQuery){
13948 if(this.editable && !this.tickable){
13949 this.inputEl().dom.select();
13953 !this.selectByValue(this.value, true) &&
13956 !this.store.lastOptions ||
13957 typeof(this.store.lastOptions.add) == 'undefined' ||
13958 this.store.lastOptions.add != true
13961 this.select(0, true);
13964 if(this.autoFocus){
13967 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13968 this.taTask.delay(this.typeAheadDelay);
13972 this.onEmptyResults();
13978 onLoadException : function()
13980 this.hasQuery = false;
13982 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13983 this.loading.hide();
13986 if(this.tickable && this.editable){
13991 // only causes errors at present
13992 //Roo.log(this.store.reader.jsonData);
13993 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13995 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14001 onTypeAhead : function(){
14002 if(this.store.getCount() > 0){
14003 var r = this.store.getAt(0);
14004 var newValue = r.data[this.displayField];
14005 var len = newValue.length;
14006 var selStart = this.getRawValue().length;
14008 if(selStart != len){
14009 this.setRawValue(newValue);
14010 this.selectText(selStart, newValue.length);
14016 onSelect : function(record, index){
14018 if(this.fireEvent('beforeselect', this, record, index) !== false){
14020 this.setFromData(index > -1 ? record.data : false);
14023 this.fireEvent('select', this, record, index);
14028 * Returns the currently selected field value or empty string if no value is set.
14029 * @return {String} value The selected value
14031 getValue : function()
14033 if(Roo.isIOS && this.useNativeIOS){
14034 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14038 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14041 if(this.valueField){
14042 return typeof this.value != 'undefined' ? this.value : '';
14044 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14048 getRawValue : function()
14050 if(Roo.isIOS && this.useNativeIOS){
14051 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14054 var v = this.inputEl().getValue();
14060 * Clears any text/value currently set in the field
14062 clearValue : function(){
14064 if(this.hiddenField){
14065 this.hiddenField.dom.value = '';
14068 this.setRawValue('');
14069 this.lastSelectionText = '';
14070 this.lastData = false;
14072 var close = this.closeTriggerEl();
14083 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14084 * will be displayed in the field. If the value does not match the data value of an existing item,
14085 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14086 * Otherwise the field will be blank (although the value will still be set).
14087 * @param {String} value The value to match
14089 setValue : function(v)
14091 if(Roo.isIOS && this.useNativeIOS){
14092 this.setIOSValue(v);
14102 if(this.valueField){
14103 var r = this.findRecord(this.valueField, v);
14105 text = r.data[this.displayField];
14106 }else if(this.valueNotFoundText !== undefined){
14107 text = this.valueNotFoundText;
14110 this.lastSelectionText = text;
14111 if(this.hiddenField){
14112 this.hiddenField.dom.value = v;
14114 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14117 var close = this.closeTriggerEl();
14120 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14126 * @property {Object} the last set data for the element
14131 * Sets the value of the field based on a object which is related to the record format for the store.
14132 * @param {Object} value the value to set as. or false on reset?
14134 setFromData : function(o){
14141 var dv = ''; // display value
14142 var vv = ''; // value value..
14144 if (this.displayField) {
14145 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14147 // this is an error condition!!!
14148 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14151 if(this.valueField){
14152 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14155 var close = this.closeTriggerEl();
14158 if(dv.length || vv * 1 > 0){
14160 this.blockFocus=true;
14166 if(this.hiddenField){
14167 this.hiddenField.dom.value = vv;
14169 this.lastSelectionText = dv;
14170 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14174 // no hidden field.. - we store the value in 'value', but still display
14175 // display field!!!!
14176 this.lastSelectionText = dv;
14177 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14184 reset : function(){
14185 // overridden so that last data is reset..
14192 this.setValue(this.originalValue);
14193 //this.clearInvalid();
14194 this.lastData = false;
14196 this.view.clearSelections();
14202 findRecord : function(prop, value){
14204 if(this.store.getCount() > 0){
14205 this.store.each(function(r){
14206 if(r.data[prop] == value){
14216 getName: function()
14218 // returns hidden if it's set..
14219 if (!this.rendered) {return ''};
14220 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14224 onViewMove : function(e, t){
14225 this.inKeyMode = false;
14229 onViewOver : function(e, t){
14230 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14233 var item = this.view.findItemFromChild(t);
14236 var index = this.view.indexOf(item);
14237 this.select(index, false);
14242 onViewClick : function(view, doFocus, el, e)
14244 var index = this.view.getSelectedIndexes()[0];
14246 var r = this.store.getAt(index);
14250 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14257 Roo.each(this.tickItems, function(v,k){
14259 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14261 _this.tickItems.splice(k, 1);
14263 if(typeof(e) == 'undefined' && view == false){
14264 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14276 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14277 this.tickItems.push(r.data);
14280 if(typeof(e) == 'undefined' && view == false){
14281 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14288 this.onSelect(r, index);
14290 if(doFocus !== false && !this.blockFocus){
14291 this.inputEl().focus();
14296 restrictHeight : function(){
14297 //this.innerList.dom.style.height = '';
14298 //var inner = this.innerList.dom;
14299 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14300 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14301 //this.list.beginUpdate();
14302 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14303 this.list.alignTo(this.inputEl(), this.listAlign);
14304 this.list.alignTo(this.inputEl(), this.listAlign);
14305 //this.list.endUpdate();
14309 onEmptyResults : function(){
14311 if(this.tickable && this.editable){
14312 this.hasFocus = false;
14313 this.restrictHeight();
14321 * Returns true if the dropdown list is expanded, else false.
14323 isExpanded : function(){
14324 return this.list.isVisible();
14328 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14329 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14330 * @param {String} value The data value of the item to select
14331 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14332 * selected item if it is not currently in view (defaults to true)
14333 * @return {Boolean} True if the value matched an item in the list, else false
14335 selectByValue : function(v, scrollIntoView){
14336 if(v !== undefined && v !== null){
14337 var r = this.findRecord(this.valueField || this.displayField, v);
14339 this.select(this.store.indexOf(r), scrollIntoView);
14347 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14348 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14349 * @param {Number} index The zero-based index of the list item to select
14350 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14351 * selected item if it is not currently in view (defaults to true)
14353 select : function(index, scrollIntoView){
14354 this.selectedIndex = index;
14355 this.view.select(index);
14356 if(scrollIntoView !== false){
14357 var el = this.view.getNode(index);
14359 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14362 this.list.scrollChildIntoView(el, false);
14368 selectNext : function(){
14369 var ct = this.store.getCount();
14371 if(this.selectedIndex == -1){
14373 }else if(this.selectedIndex < ct-1){
14374 this.select(this.selectedIndex+1);
14380 selectPrev : function(){
14381 var ct = this.store.getCount();
14383 if(this.selectedIndex == -1){
14385 }else if(this.selectedIndex != 0){
14386 this.select(this.selectedIndex-1);
14392 onKeyUp : function(e){
14393 if(this.editable !== false && !e.isSpecialKey()){
14394 this.lastKey = e.getKey();
14395 this.dqTask.delay(this.queryDelay);
14400 validateBlur : function(){
14401 return !this.list || !this.list.isVisible();
14405 initQuery : function(){
14407 var v = this.getRawValue();
14409 if(this.tickable && this.editable){
14410 v = this.tickableInputEl().getValue();
14417 doForce : function(){
14418 if(this.inputEl().dom.value.length > 0){
14419 this.inputEl().dom.value =
14420 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14426 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14427 * query allowing the query action to be canceled if needed.
14428 * @param {String} query The SQL query to execute
14429 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14430 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14431 * saved in the current store (defaults to false)
14433 doQuery : function(q, forceAll){
14435 if(q === undefined || q === null){
14440 forceAll: forceAll,
14444 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14449 forceAll = qe.forceAll;
14450 if(forceAll === true || (q.length >= this.minChars)){
14452 this.hasQuery = true;
14454 if(this.lastQuery != q || this.alwaysQuery){
14455 this.lastQuery = q;
14456 if(this.mode == 'local'){
14457 this.selectedIndex = -1;
14459 this.store.clearFilter();
14462 if(this.specialFilter){
14463 this.fireEvent('specialfilter', this);
14468 this.store.filter(this.displayField, q);
14471 this.store.fireEvent("datachanged", this.store);
14478 this.store.baseParams[this.queryParam] = q;
14480 var options = {params : this.getParams(q)};
14483 options.add = true;
14484 options.params.start = this.page * this.pageSize;
14487 this.store.load(options);
14490 * this code will make the page width larger, at the beginning, the list not align correctly,
14491 * we should expand the list on onLoad
14492 * so command out it
14497 this.selectedIndex = -1;
14502 this.loadNext = false;
14506 getParams : function(q){
14508 //p[this.queryParam] = q;
14512 p.limit = this.pageSize;
14518 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14520 collapse : function(){
14521 if(!this.isExpanded()){
14527 this.hasFocus = false;
14531 this.cancelBtn.hide();
14532 this.trigger.show();
14535 this.tickableInputEl().dom.value = '';
14536 this.tickableInputEl().blur();
14541 Roo.get(document).un('mousedown', this.collapseIf, this);
14542 Roo.get(document).un('mousewheel', this.collapseIf, this);
14543 if (!this.editable) {
14544 Roo.get(document).un('keydown', this.listKeyPress, this);
14546 this.fireEvent('collapse', this);
14552 collapseIf : function(e){
14553 var in_combo = e.within(this.el);
14554 var in_list = e.within(this.list);
14555 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14557 if (in_combo || in_list || is_list) {
14558 //e.stopPropagation();
14563 this.onTickableFooterButtonClick(e, false, false);
14571 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14573 expand : function(){
14575 if(this.isExpanded() || !this.hasFocus){
14579 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14580 this.list.setWidth(lw);
14586 this.restrictHeight();
14590 this.tickItems = Roo.apply([], this.item);
14593 this.cancelBtn.show();
14594 this.trigger.hide();
14597 this.tickableInputEl().focus();
14602 Roo.get(document).on('mousedown', this.collapseIf, this);
14603 Roo.get(document).on('mousewheel', this.collapseIf, this);
14604 if (!this.editable) {
14605 Roo.get(document).on('keydown', this.listKeyPress, this);
14608 this.fireEvent('expand', this);
14612 // Implements the default empty TriggerField.onTriggerClick function
14613 onTriggerClick : function(e)
14615 Roo.log('trigger click');
14617 if(this.disabled || !this.triggerList){
14622 this.loadNext = false;
14624 if(this.isExpanded()){
14626 if (!this.blockFocus) {
14627 this.inputEl().focus();
14631 this.hasFocus = true;
14632 if(this.triggerAction == 'all') {
14633 this.doQuery(this.allQuery, true);
14635 this.doQuery(this.getRawValue());
14637 if (!this.blockFocus) {
14638 this.inputEl().focus();
14643 onTickableTriggerClick : function(e)
14650 this.loadNext = false;
14651 this.hasFocus = true;
14653 if(this.triggerAction == 'all') {
14654 this.doQuery(this.allQuery, true);
14656 this.doQuery(this.getRawValue());
14660 onSearchFieldClick : function(e)
14662 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14663 this.onTickableFooterButtonClick(e, false, false);
14667 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14672 this.loadNext = false;
14673 this.hasFocus = true;
14675 if(this.triggerAction == 'all') {
14676 this.doQuery(this.allQuery, true);
14678 this.doQuery(this.getRawValue());
14682 listKeyPress : function(e)
14684 //Roo.log('listkeypress');
14685 // scroll to first matching element based on key pres..
14686 if (e.isSpecialKey()) {
14689 var k = String.fromCharCode(e.getKey()).toUpperCase();
14692 var csel = this.view.getSelectedNodes();
14693 var cselitem = false;
14695 var ix = this.view.indexOf(csel[0]);
14696 cselitem = this.store.getAt(ix);
14697 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14703 this.store.each(function(v) {
14705 // start at existing selection.
14706 if (cselitem.id == v.id) {
14712 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14713 match = this.store.indexOf(v);
14719 if (match === false) {
14720 return true; // no more action?
14723 this.view.select(match);
14724 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14725 sn.scrollIntoView(sn.dom.parentNode, false);
14728 onViewScroll : function(e, t){
14730 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){
14734 this.hasQuery = true;
14736 this.loading = this.list.select('.loading', true).first();
14738 if(this.loading === null){
14739 this.list.createChild({
14741 cls: 'loading roo-select2-more-results roo-select2-active',
14742 html: 'Loading more results...'
14745 this.loading = this.list.select('.loading', true).first();
14747 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14749 this.loading.hide();
14752 this.loading.show();
14757 this.loadNext = true;
14759 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14764 addItem : function(o)
14766 var dv = ''; // display value
14768 if (this.displayField) {
14769 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14771 // this is an error condition!!!
14772 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14779 var choice = this.choices.createChild({
14781 cls: 'roo-select2-search-choice',
14790 cls: 'roo-select2-search-choice-close fa fa-times',
14795 }, this.searchField);
14797 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14799 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14807 this.inputEl().dom.value = '';
14812 onRemoveItem : function(e, _self, o)
14814 e.preventDefault();
14816 this.lastItem = Roo.apply([], this.item);
14818 var index = this.item.indexOf(o.data) * 1;
14821 Roo.log('not this item?!');
14825 this.item.splice(index, 1);
14830 this.fireEvent('remove', this, e);
14836 syncValue : function()
14838 if(!this.item.length){
14845 Roo.each(this.item, function(i){
14846 if(_this.valueField){
14847 value.push(i[_this.valueField]);
14854 this.value = value.join(',');
14856 if(this.hiddenField){
14857 this.hiddenField.dom.value = this.value;
14860 this.store.fireEvent("datachanged", this.store);
14865 clearItem : function()
14867 if(!this.multiple){
14873 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14881 if(this.tickable && !Roo.isTouch){
14882 this.view.refresh();
14886 inputEl: function ()
14888 if(Roo.isIOS && this.useNativeIOS){
14889 return this.el.select('select.roo-ios-select', true).first();
14892 if(Roo.isTouch && this.mobileTouchView){
14893 return this.el.select('input.form-control',true).first();
14897 return this.searchField;
14900 return this.el.select('input.form-control',true).first();
14903 onTickableFooterButtonClick : function(e, btn, el)
14905 e.preventDefault();
14907 this.lastItem = Roo.apply([], this.item);
14909 if(btn && btn.name == 'cancel'){
14910 this.tickItems = Roo.apply([], this.item);
14919 Roo.each(this.tickItems, function(o){
14927 validate : function()
14929 if(this.getVisibilityEl().hasClass('hidden')){
14933 var v = this.getRawValue();
14936 v = this.getValue();
14939 if(this.disabled || this.allowBlank || v.length){
14944 this.markInvalid();
14948 tickableInputEl : function()
14950 if(!this.tickable || !this.editable){
14951 return this.inputEl();
14954 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14958 getAutoCreateTouchView : function()
14963 cls: 'form-group' //input-group
14969 type : this.inputType,
14970 cls : 'form-control x-combo-noedit',
14971 autocomplete: 'new-password',
14972 placeholder : this.placeholder || '',
14977 input.name = this.name;
14981 input.cls += ' input-' + this.size;
14984 if (this.disabled) {
14985 input.disabled = true;
14996 inputblock.cls += ' input-group';
14998 inputblock.cn.unshift({
15000 cls : 'input-group-addon',
15005 if(this.removable && !this.multiple){
15006 inputblock.cls += ' roo-removable';
15008 inputblock.cn.push({
15011 cls : 'roo-combo-removable-btn close'
15015 if(this.hasFeedback && !this.allowBlank){
15017 inputblock.cls += ' has-feedback';
15019 inputblock.cn.push({
15021 cls: 'glyphicon form-control-feedback'
15028 inputblock.cls += (this.before) ? '' : ' input-group';
15030 inputblock.cn.push({
15032 cls : 'input-group-addon',
15043 cls: 'form-hidden-field'
15057 cls: 'form-hidden-field'
15061 cls: 'roo-select2-choices',
15065 cls: 'roo-select2-search-field',
15078 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15084 if(!this.multiple && this.showToggleBtn){
15091 if (this.caret != false) {
15094 cls: 'fa fa-' + this.caret
15101 cls : 'input-group-addon btn dropdown-toggle',
15106 cls: 'combobox-clear',
15120 combobox.cls += ' roo-select2-container-multi';
15123 var align = this.labelAlign || this.parentLabelAlign();
15125 if (align ==='left' && this.fieldLabel.length) {
15130 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15131 tooltip : 'This field is required'
15135 cls : 'control-label',
15136 html : this.fieldLabel
15147 var labelCfg = cfg.cn[1];
15148 var contentCfg = cfg.cn[2];
15151 if(this.indicatorpos == 'right'){
15156 cls : 'control-label',
15160 html : this.fieldLabel
15164 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15165 tooltip : 'This field is required'
15178 labelCfg = cfg.cn[0];
15179 contentCfg = cfg.cn[1];
15184 if(this.labelWidth > 12){
15185 labelCfg.style = "width: " + this.labelWidth + 'px';
15188 if(this.labelWidth < 13 && this.labelmd == 0){
15189 this.labelmd = this.labelWidth;
15192 if(this.labellg > 0){
15193 labelCfg.cls += ' col-lg-' + this.labellg;
15194 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15197 if(this.labelmd > 0){
15198 labelCfg.cls += ' col-md-' + this.labelmd;
15199 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15202 if(this.labelsm > 0){
15203 labelCfg.cls += ' col-sm-' + this.labelsm;
15204 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15207 if(this.labelxs > 0){
15208 labelCfg.cls += ' col-xs-' + this.labelxs;
15209 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15213 } else if ( this.fieldLabel.length) {
15217 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15218 tooltip : 'This field is required'
15222 cls : 'control-label',
15223 html : this.fieldLabel
15234 if(this.indicatorpos == 'right'){
15238 cls : 'control-label',
15239 html : this.fieldLabel,
15243 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15244 tooltip : 'This field is required'
15261 var settings = this;
15263 ['xs','sm','md','lg'].map(function(size){
15264 if (settings[size]) {
15265 cfg.cls += ' col-' + size + '-' + settings[size];
15272 initTouchView : function()
15274 this.renderTouchView();
15276 this.touchViewEl.on('scroll', function(){
15277 this.el.dom.scrollTop = 0;
15280 this.originalValue = this.getValue();
15282 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15284 this.inputEl().on("click", this.showTouchView, this);
15285 if (this.triggerEl) {
15286 this.triggerEl.on("click", this.showTouchView, this);
15290 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15291 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15293 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15295 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15296 this.store.on('load', this.onTouchViewLoad, this);
15297 this.store.on('loadexception', this.onTouchViewLoadException, this);
15299 if(this.hiddenName){
15301 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15303 this.hiddenField.dom.value =
15304 this.hiddenValue !== undefined ? this.hiddenValue :
15305 this.value !== undefined ? this.value : '';
15307 this.el.dom.removeAttribute('name');
15308 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15312 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15313 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15316 if(this.removable && !this.multiple){
15317 var close = this.closeTriggerEl();
15319 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15320 close.on('click', this.removeBtnClick, this, close);
15324 * fix the bug in Safari iOS8
15326 this.inputEl().on("focus", function(e){
15327 document.activeElement.blur();
15330 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15337 renderTouchView : function()
15339 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15340 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15342 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15343 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15345 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15346 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15347 this.touchViewBodyEl.setStyle('overflow', 'auto');
15349 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15350 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15352 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15353 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15357 showTouchView : function()
15363 this.touchViewHeaderEl.hide();
15365 if(this.modalTitle.length){
15366 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15367 this.touchViewHeaderEl.show();
15370 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15371 this.touchViewEl.show();
15373 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15375 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15376 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15378 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15380 if(this.modalTitle.length){
15381 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15384 this.touchViewBodyEl.setHeight(bodyHeight);
15388 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15390 this.touchViewEl.addClass('in');
15393 if(this._touchViewMask){
15394 Roo.get(document.body).addClass("x-body-masked");
15395 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15396 this._touchViewMask.setStyle('z-index', 10000);
15397 this._touchViewMask.addClass('show');
15400 this.doTouchViewQuery();
15404 hideTouchView : function()
15406 this.touchViewEl.removeClass('in');
15410 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15412 this.touchViewEl.setStyle('display', 'none');
15415 if(this._touchViewMask){
15416 this._touchViewMask.removeClass('show');
15417 Roo.get(document.body).removeClass("x-body-masked");
15421 setTouchViewValue : function()
15428 Roo.each(this.tickItems, function(o){
15433 this.hideTouchView();
15436 doTouchViewQuery : function()
15445 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15449 if(!this.alwaysQuery || this.mode == 'local'){
15450 this.onTouchViewLoad();
15457 onTouchViewBeforeLoad : function(combo,opts)
15463 onTouchViewLoad : function()
15465 if(this.store.getCount() < 1){
15466 this.onTouchViewEmptyResults();
15470 this.clearTouchView();
15472 var rawValue = this.getRawValue();
15474 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15476 this.tickItems = [];
15478 this.store.data.each(function(d, rowIndex){
15479 var row = this.touchViewListGroup.createChild(template);
15481 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15482 row.addClass(d.data.cls);
15485 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15488 html : d.data[this.displayField]
15491 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15492 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15495 row.removeClass('selected');
15496 if(!this.multiple && this.valueField &&
15497 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15500 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15501 row.addClass('selected');
15504 if(this.multiple && this.valueField &&
15505 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15509 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15510 this.tickItems.push(d.data);
15513 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15517 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15519 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15521 if(this.modalTitle.length){
15522 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15525 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15527 if(this.mobile_restrict_height && listHeight < bodyHeight){
15528 this.touchViewBodyEl.setHeight(listHeight);
15533 if(firstChecked && listHeight > bodyHeight){
15534 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15539 onTouchViewLoadException : function()
15541 this.hideTouchView();
15544 onTouchViewEmptyResults : function()
15546 this.clearTouchView();
15548 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15550 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15554 clearTouchView : function()
15556 this.touchViewListGroup.dom.innerHTML = '';
15559 onTouchViewClick : function(e, el, o)
15561 e.preventDefault();
15564 var rowIndex = o.rowIndex;
15566 var r = this.store.getAt(rowIndex);
15568 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15570 if(!this.multiple){
15571 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15572 c.dom.removeAttribute('checked');
15575 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15577 this.setFromData(r.data);
15579 var close = this.closeTriggerEl();
15585 this.hideTouchView();
15587 this.fireEvent('select', this, r, rowIndex);
15592 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15593 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15594 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15598 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15599 this.addItem(r.data);
15600 this.tickItems.push(r.data);
15604 getAutoCreateNativeIOS : function()
15607 cls: 'form-group' //input-group,
15612 cls : 'roo-ios-select'
15616 combobox.name = this.name;
15619 if (this.disabled) {
15620 combobox.disabled = true;
15623 var settings = this;
15625 ['xs','sm','md','lg'].map(function(size){
15626 if (settings[size]) {
15627 cfg.cls += ' col-' + size + '-' + settings[size];
15637 initIOSView : function()
15639 this.store.on('load', this.onIOSViewLoad, this);
15644 onIOSViewLoad : function()
15646 if(this.store.getCount() < 1){
15650 this.clearIOSView();
15652 if(this.allowBlank) {
15654 var default_text = '-- SELECT --';
15656 if(this.placeholder.length){
15657 default_text = this.placeholder;
15660 if(this.emptyTitle.length){
15661 default_text += ' - ' + this.emptyTitle + ' -';
15664 var opt = this.inputEl().createChild({
15667 html : default_text
15671 o[this.valueField] = 0;
15672 o[this.displayField] = default_text;
15674 this.ios_options.push({
15681 this.store.data.each(function(d, rowIndex){
15685 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15686 html = d.data[this.displayField];
15691 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15692 value = d.data[this.valueField];
15701 if(this.value == d.data[this.valueField]){
15702 option['selected'] = true;
15705 var opt = this.inputEl().createChild(option);
15707 this.ios_options.push({
15714 this.inputEl().on('change', function(){
15715 this.fireEvent('select', this);
15720 clearIOSView: function()
15722 this.inputEl().dom.innerHTML = '';
15724 this.ios_options = [];
15727 setIOSValue: function(v)
15731 if(!this.ios_options){
15735 Roo.each(this.ios_options, function(opts){
15737 opts.el.dom.removeAttribute('selected');
15739 if(opts.data[this.valueField] != v){
15743 opts.el.dom.setAttribute('selected', true);
15749 * @cfg {Boolean} grow
15753 * @cfg {Number} growMin
15757 * @cfg {Number} growMax
15766 Roo.apply(Roo.bootstrap.ComboBox, {
15770 cls: 'modal-header',
15792 cls: 'list-group-item',
15796 cls: 'roo-combobox-list-group-item-value'
15800 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15814 listItemCheckbox : {
15816 cls: 'list-group-item',
15820 cls: 'roo-combobox-list-group-item-value'
15824 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15840 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15845 cls: 'modal-footer',
15853 cls: 'col-xs-6 text-left',
15856 cls: 'btn btn-danger roo-touch-view-cancel',
15862 cls: 'col-xs-6 text-right',
15865 cls: 'btn btn-success roo-touch-view-ok',
15876 Roo.apply(Roo.bootstrap.ComboBox, {
15878 touchViewTemplate : {
15880 cls: 'modal fade roo-combobox-touch-view',
15884 cls: 'modal-dialog',
15885 style : 'position:fixed', // we have to fix position....
15889 cls: 'modal-content',
15891 Roo.bootstrap.ComboBox.header,
15892 Roo.bootstrap.ComboBox.body,
15893 Roo.bootstrap.ComboBox.footer
15902 * Ext JS Library 1.1.1
15903 * Copyright(c) 2006-2007, Ext JS, LLC.
15905 * Originally Released Under LGPL - original licence link has changed is not relivant.
15908 * <script type="text/javascript">
15913 * @extends Roo.util.Observable
15914 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15915 * This class also supports single and multi selection modes. <br>
15916 * Create a data model bound view:
15918 var store = new Roo.data.Store(...);
15920 var view = new Roo.View({
15922 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15924 singleSelect: true,
15925 selectedClass: "ydataview-selected",
15929 // listen for node click?
15930 view.on("click", function(vw, index, node, e){
15931 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15935 dataModel.load("foobar.xml");
15937 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15939 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15940 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15942 * Note: old style constructor is still suported (container, template, config)
15945 * Create a new View
15946 * @param {Object} config The config object
15949 Roo.View = function(config, depreciated_tpl, depreciated_config){
15951 this.parent = false;
15953 if (typeof(depreciated_tpl) == 'undefined') {
15954 // new way.. - universal constructor.
15955 Roo.apply(this, config);
15956 this.el = Roo.get(this.el);
15959 this.el = Roo.get(config);
15960 this.tpl = depreciated_tpl;
15961 Roo.apply(this, depreciated_config);
15963 this.wrapEl = this.el.wrap().wrap();
15964 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15967 if(typeof(this.tpl) == "string"){
15968 this.tpl = new Roo.Template(this.tpl);
15970 // support xtype ctors..
15971 this.tpl = new Roo.factory(this.tpl, Roo);
15975 this.tpl.compile();
15980 * @event beforeclick
15981 * Fires before a click is processed. Returns false to cancel the default action.
15982 * @param {Roo.View} this
15983 * @param {Number} index The index of the target node
15984 * @param {HTMLElement} node The target node
15985 * @param {Roo.EventObject} e The raw event object
15987 "beforeclick" : true,
15990 * Fires when a template node is clicked.
15991 * @param {Roo.View} this
15992 * @param {Number} index The index of the target node
15993 * @param {HTMLElement} node The target node
15994 * @param {Roo.EventObject} e The raw event object
15999 * Fires when a template node is double clicked.
16000 * @param {Roo.View} this
16001 * @param {Number} index The index of the target node
16002 * @param {HTMLElement} node The target node
16003 * @param {Roo.EventObject} e The raw event object
16007 * @event contextmenu
16008 * Fires when a template node is right clicked.
16009 * @param {Roo.View} this
16010 * @param {Number} index The index of the target node
16011 * @param {HTMLElement} node The target node
16012 * @param {Roo.EventObject} e The raw event object
16014 "contextmenu" : true,
16016 * @event selectionchange
16017 * Fires when the selected nodes change.
16018 * @param {Roo.View} this
16019 * @param {Array} selections Array of the selected nodes
16021 "selectionchange" : true,
16024 * @event beforeselect
16025 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16026 * @param {Roo.View} this
16027 * @param {HTMLElement} node The node to be selected
16028 * @param {Array} selections Array of currently selected nodes
16030 "beforeselect" : true,
16032 * @event preparedata
16033 * Fires on every row to render, to allow you to change the data.
16034 * @param {Roo.View} this
16035 * @param {Object} data to be rendered (change this)
16037 "preparedata" : true
16045 "click": this.onClick,
16046 "dblclick": this.onDblClick,
16047 "contextmenu": this.onContextMenu,
16051 this.selections = [];
16053 this.cmp = new Roo.CompositeElementLite([]);
16055 this.store = Roo.factory(this.store, Roo.data);
16056 this.setStore(this.store, true);
16059 if ( this.footer && this.footer.xtype) {
16061 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16063 this.footer.dataSource = this.store;
16064 this.footer.container = fctr;
16065 this.footer = Roo.factory(this.footer, Roo);
16066 fctr.insertFirst(this.el);
16068 // this is a bit insane - as the paging toolbar seems to detach the el..
16069 // dom.parentNode.parentNode.parentNode
16070 // they get detached?
16074 Roo.View.superclass.constructor.call(this);
16079 Roo.extend(Roo.View, Roo.util.Observable, {
16082 * @cfg {Roo.data.Store} store Data store to load data from.
16087 * @cfg {String|Roo.Element} el The container element.
16092 * @cfg {String|Roo.Template} tpl The template used by this View
16096 * @cfg {String} dataName the named area of the template to use as the data area
16097 * Works with domtemplates roo-name="name"
16101 * @cfg {String} selectedClass The css class to add to selected nodes
16103 selectedClass : "x-view-selected",
16105 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16110 * @cfg {String} text to display on mask (default Loading)
16114 * @cfg {Boolean} multiSelect Allow multiple selection
16116 multiSelect : false,
16118 * @cfg {Boolean} singleSelect Allow single selection
16120 singleSelect: false,
16123 * @cfg {Boolean} toggleSelect - selecting
16125 toggleSelect : false,
16128 * @cfg {Boolean} tickable - selecting
16133 * Returns the element this view is bound to.
16134 * @return {Roo.Element}
16136 getEl : function(){
16137 return this.wrapEl;
16143 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16145 refresh : function(){
16146 //Roo.log('refresh');
16149 // if we are using something like 'domtemplate', then
16150 // the what gets used is:
16151 // t.applySubtemplate(NAME, data, wrapping data..)
16152 // the outer template then get' applied with
16153 // the store 'extra data'
16154 // and the body get's added to the
16155 // roo-name="data" node?
16156 // <span class='roo-tpl-{name}'></span> ?????
16160 this.clearSelections();
16161 this.el.update("");
16163 var records = this.store.getRange();
16164 if(records.length < 1) {
16166 // is this valid?? = should it render a template??
16168 this.el.update(this.emptyText);
16172 if (this.dataName) {
16173 this.el.update(t.apply(this.store.meta)); //????
16174 el = this.el.child('.roo-tpl-' + this.dataName);
16177 for(var i = 0, len = records.length; i < len; i++){
16178 var data = this.prepareData(records[i].data, i, records[i]);
16179 this.fireEvent("preparedata", this, data, i, records[i]);
16181 var d = Roo.apply({}, data);
16184 Roo.apply(d, {'roo-id' : Roo.id()});
16188 Roo.each(this.parent.item, function(item){
16189 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16192 Roo.apply(d, {'roo-data-checked' : 'checked'});
16196 html[html.length] = Roo.util.Format.trim(
16198 t.applySubtemplate(this.dataName, d, this.store.meta) :
16205 el.update(html.join(""));
16206 this.nodes = el.dom.childNodes;
16207 this.updateIndexes(0);
16212 * Function to override to reformat the data that is sent to
16213 * the template for each node.
16214 * DEPRICATED - use the preparedata event handler.
16215 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16216 * a JSON object for an UpdateManager bound view).
16218 prepareData : function(data, index, record)
16220 this.fireEvent("preparedata", this, data, index, record);
16224 onUpdate : function(ds, record){
16225 // Roo.log('on update');
16226 this.clearSelections();
16227 var index = this.store.indexOf(record);
16228 var n = this.nodes[index];
16229 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16230 n.parentNode.removeChild(n);
16231 this.updateIndexes(index, index);
16237 onAdd : function(ds, records, index)
16239 //Roo.log(['on Add', ds, records, index] );
16240 this.clearSelections();
16241 if(this.nodes.length == 0){
16245 var n = this.nodes[index];
16246 for(var i = 0, len = records.length; i < len; i++){
16247 var d = this.prepareData(records[i].data, i, records[i]);
16249 this.tpl.insertBefore(n, d);
16252 this.tpl.append(this.el, d);
16255 this.updateIndexes(index);
16258 onRemove : function(ds, record, index){
16259 // Roo.log('onRemove');
16260 this.clearSelections();
16261 var el = this.dataName ?
16262 this.el.child('.roo-tpl-' + this.dataName) :
16265 el.dom.removeChild(this.nodes[index]);
16266 this.updateIndexes(index);
16270 * Refresh an individual node.
16271 * @param {Number} index
16273 refreshNode : function(index){
16274 this.onUpdate(this.store, this.store.getAt(index));
16277 updateIndexes : function(startIndex, endIndex){
16278 var ns = this.nodes;
16279 startIndex = startIndex || 0;
16280 endIndex = endIndex || ns.length - 1;
16281 for(var i = startIndex; i <= endIndex; i++){
16282 ns[i].nodeIndex = i;
16287 * Changes the data store this view uses and refresh the view.
16288 * @param {Store} store
16290 setStore : function(store, initial){
16291 if(!initial && this.store){
16292 this.store.un("datachanged", this.refresh);
16293 this.store.un("add", this.onAdd);
16294 this.store.un("remove", this.onRemove);
16295 this.store.un("update", this.onUpdate);
16296 this.store.un("clear", this.refresh);
16297 this.store.un("beforeload", this.onBeforeLoad);
16298 this.store.un("load", this.onLoad);
16299 this.store.un("loadexception", this.onLoad);
16303 store.on("datachanged", this.refresh, this);
16304 store.on("add", this.onAdd, this);
16305 store.on("remove", this.onRemove, this);
16306 store.on("update", this.onUpdate, this);
16307 store.on("clear", this.refresh, this);
16308 store.on("beforeload", this.onBeforeLoad, this);
16309 store.on("load", this.onLoad, this);
16310 store.on("loadexception", this.onLoad, this);
16318 * onbeforeLoad - masks the loading area.
16321 onBeforeLoad : function(store,opts)
16323 //Roo.log('onBeforeLoad');
16325 this.el.update("");
16327 this.el.mask(this.mask ? this.mask : "Loading" );
16329 onLoad : function ()
16336 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16337 * @param {HTMLElement} node
16338 * @return {HTMLElement} The template node
16340 findItemFromChild : function(node){
16341 var el = this.dataName ?
16342 this.el.child('.roo-tpl-' + this.dataName,true) :
16345 if(!node || node.parentNode == el){
16348 var p = node.parentNode;
16349 while(p && p != el){
16350 if(p.parentNode == el){
16359 onClick : function(e){
16360 var item = this.findItemFromChild(e.getTarget());
16362 var index = this.indexOf(item);
16363 if(this.onItemClick(item, index, e) !== false){
16364 this.fireEvent("click", this, index, item, e);
16367 this.clearSelections();
16372 onContextMenu : function(e){
16373 var item = this.findItemFromChild(e.getTarget());
16375 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16380 onDblClick : function(e){
16381 var item = this.findItemFromChild(e.getTarget());
16383 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16387 onItemClick : function(item, index, e)
16389 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16392 if (this.toggleSelect) {
16393 var m = this.isSelected(item) ? 'unselect' : 'select';
16396 _t[m](item, true, false);
16399 if(this.multiSelect || this.singleSelect){
16400 if(this.multiSelect && e.shiftKey && this.lastSelection){
16401 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16403 this.select(item, this.multiSelect && e.ctrlKey);
16404 this.lastSelection = item;
16407 if(!this.tickable){
16408 e.preventDefault();
16416 * Get the number of selected nodes.
16419 getSelectionCount : function(){
16420 return this.selections.length;
16424 * Get the currently selected nodes.
16425 * @return {Array} An array of HTMLElements
16427 getSelectedNodes : function(){
16428 return this.selections;
16432 * Get the indexes of the selected nodes.
16435 getSelectedIndexes : function(){
16436 var indexes = [], s = this.selections;
16437 for(var i = 0, len = s.length; i < len; i++){
16438 indexes.push(s[i].nodeIndex);
16444 * Clear all selections
16445 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16447 clearSelections : function(suppressEvent){
16448 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16449 this.cmp.elements = this.selections;
16450 this.cmp.removeClass(this.selectedClass);
16451 this.selections = [];
16452 if(!suppressEvent){
16453 this.fireEvent("selectionchange", this, this.selections);
16459 * Returns true if the passed node is selected
16460 * @param {HTMLElement/Number} node The node or node index
16461 * @return {Boolean}
16463 isSelected : function(node){
16464 var s = this.selections;
16468 node = this.getNode(node);
16469 return s.indexOf(node) !== -1;
16474 * @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
16475 * @param {Boolean} keepExisting (optional) true to keep existing selections
16476 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16478 select : function(nodeInfo, keepExisting, suppressEvent){
16479 if(nodeInfo instanceof Array){
16481 this.clearSelections(true);
16483 for(var i = 0, len = nodeInfo.length; i < len; i++){
16484 this.select(nodeInfo[i], true, true);
16488 var node = this.getNode(nodeInfo);
16489 if(!node || this.isSelected(node)){
16490 return; // already selected.
16493 this.clearSelections(true);
16496 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16497 Roo.fly(node).addClass(this.selectedClass);
16498 this.selections.push(node);
16499 if(!suppressEvent){
16500 this.fireEvent("selectionchange", this, this.selections);
16508 * @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
16509 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16510 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16512 unselect : function(nodeInfo, keepExisting, suppressEvent)
16514 if(nodeInfo instanceof Array){
16515 Roo.each(this.selections, function(s) {
16516 this.unselect(s, nodeInfo);
16520 var node = this.getNode(nodeInfo);
16521 if(!node || !this.isSelected(node)){
16522 //Roo.log("not selected");
16523 return; // not selected.
16527 Roo.each(this.selections, function(s) {
16529 Roo.fly(node).removeClass(this.selectedClass);
16536 this.selections= ns;
16537 this.fireEvent("selectionchange", this, this.selections);
16541 * Gets a template node.
16542 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16543 * @return {HTMLElement} The node or null if it wasn't found
16545 getNode : function(nodeInfo){
16546 if(typeof nodeInfo == "string"){
16547 return document.getElementById(nodeInfo);
16548 }else if(typeof nodeInfo == "number"){
16549 return this.nodes[nodeInfo];
16555 * Gets a range template nodes.
16556 * @param {Number} startIndex
16557 * @param {Number} endIndex
16558 * @return {Array} An array of nodes
16560 getNodes : function(start, end){
16561 var ns = this.nodes;
16562 start = start || 0;
16563 end = typeof end == "undefined" ? ns.length - 1 : end;
16566 for(var i = start; i <= end; i++){
16570 for(var i = start; i >= end; i--){
16578 * Finds the index of the passed node
16579 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16580 * @return {Number} The index of the node or -1
16582 indexOf : function(node){
16583 node = this.getNode(node);
16584 if(typeof node.nodeIndex == "number"){
16585 return node.nodeIndex;
16587 var ns = this.nodes;
16588 for(var i = 0, len = ns.length; i < len; i++){
16599 * based on jquery fullcalendar
16603 Roo.bootstrap = Roo.bootstrap || {};
16605 * @class Roo.bootstrap.Calendar
16606 * @extends Roo.bootstrap.Component
16607 * Bootstrap Calendar class
16608 * @cfg {Boolean} loadMask (true|false) default false
16609 * @cfg {Object} header generate the user specific header of the calendar, default false
16612 * Create a new Container
16613 * @param {Object} config The config object
16618 Roo.bootstrap.Calendar = function(config){
16619 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16623 * Fires when a date is selected
16624 * @param {DatePicker} this
16625 * @param {Date} date The selected date
16629 * @event monthchange
16630 * Fires when the displayed month changes
16631 * @param {DatePicker} this
16632 * @param {Date} date The selected month
16634 'monthchange': true,
16636 * @event evententer
16637 * Fires when mouse over an event
16638 * @param {Calendar} this
16639 * @param {event} Event
16641 'evententer': true,
16643 * @event eventleave
16644 * Fires when the mouse leaves an
16645 * @param {Calendar} this
16648 'eventleave': true,
16650 * @event eventclick
16651 * Fires when the mouse click an
16652 * @param {Calendar} this
16661 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16664 * @cfg {Number} startDay
16665 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16673 getAutoCreate : function(){
16676 var fc_button = function(name, corner, style, content ) {
16677 return Roo.apply({},{
16679 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16681 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16684 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16695 style : 'width:100%',
16702 cls : 'fc-header-left',
16704 fc_button('prev', 'left', 'arrow', '‹' ),
16705 fc_button('next', 'right', 'arrow', '›' ),
16706 { tag: 'span', cls: 'fc-header-space' },
16707 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16715 cls : 'fc-header-center',
16719 cls: 'fc-header-title',
16722 html : 'month / year'
16730 cls : 'fc-header-right',
16732 /* fc_button('month', 'left', '', 'month' ),
16733 fc_button('week', '', '', 'week' ),
16734 fc_button('day', 'right', '', 'day' )
16746 header = this.header;
16749 var cal_heads = function() {
16751 // fixme - handle this.
16753 for (var i =0; i < Date.dayNames.length; i++) {
16754 var d = Date.dayNames[i];
16757 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16758 html : d.substring(0,3)
16762 ret[0].cls += ' fc-first';
16763 ret[6].cls += ' fc-last';
16766 var cal_cell = function(n) {
16769 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16774 cls: 'fc-day-number',
16778 cls: 'fc-day-content',
16782 style: 'position: relative;' // height: 17px;
16794 var cal_rows = function() {
16797 for (var r = 0; r < 6; r++) {
16804 for (var i =0; i < Date.dayNames.length; i++) {
16805 var d = Date.dayNames[i];
16806 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16809 row.cn[0].cls+=' fc-first';
16810 row.cn[0].cn[0].style = 'min-height:90px';
16811 row.cn[6].cls+=' fc-last';
16815 ret[0].cls += ' fc-first';
16816 ret[4].cls += ' fc-prev-last';
16817 ret[5].cls += ' fc-last';
16824 cls: 'fc-border-separate',
16825 style : 'width:100%',
16833 cls : 'fc-first fc-last',
16851 cls : 'fc-content',
16852 style : "position: relative;",
16855 cls : 'fc-view fc-view-month fc-grid',
16856 style : 'position: relative',
16857 unselectable : 'on',
16860 cls : 'fc-event-container',
16861 style : 'position:absolute;z-index:8;top:0;left:0;'
16879 initEvents : function()
16882 throw "can not find store for calendar";
16888 style: "text-align:center",
16892 style: "background-color:white;width:50%;margin:250 auto",
16896 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16907 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16909 var size = this.el.select('.fc-content', true).first().getSize();
16910 this.maskEl.setSize(size.width, size.height);
16911 this.maskEl.enableDisplayMode("block");
16912 if(!this.loadMask){
16913 this.maskEl.hide();
16916 this.store = Roo.factory(this.store, Roo.data);
16917 this.store.on('load', this.onLoad, this);
16918 this.store.on('beforeload', this.onBeforeLoad, this);
16922 this.cells = this.el.select('.fc-day',true);
16923 //Roo.log(this.cells);
16924 this.textNodes = this.el.query('.fc-day-number');
16925 this.cells.addClassOnOver('fc-state-hover');
16927 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16928 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16929 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16930 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16932 this.on('monthchange', this.onMonthChange, this);
16934 this.update(new Date().clearTime());
16937 resize : function() {
16938 var sz = this.el.getSize();
16940 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16941 this.el.select('.fc-day-content div',true).setHeight(34);
16946 showPrevMonth : function(e){
16947 this.update(this.activeDate.add("mo", -1));
16949 showToday : function(e){
16950 this.update(new Date().clearTime());
16953 showNextMonth : function(e){
16954 this.update(this.activeDate.add("mo", 1));
16958 showPrevYear : function(){
16959 this.update(this.activeDate.add("y", -1));
16963 showNextYear : function(){
16964 this.update(this.activeDate.add("y", 1));
16969 update : function(date)
16971 var vd = this.activeDate;
16972 this.activeDate = date;
16973 // if(vd && this.el){
16974 // var t = date.getTime();
16975 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16976 // Roo.log('using add remove');
16978 // this.fireEvent('monthchange', this, date);
16980 // this.cells.removeClass("fc-state-highlight");
16981 // this.cells.each(function(c){
16982 // if(c.dateValue == t){
16983 // c.addClass("fc-state-highlight");
16984 // setTimeout(function(){
16985 // try{c.dom.firstChild.focus();}catch(e){}
16995 var days = date.getDaysInMonth();
16997 var firstOfMonth = date.getFirstDateOfMonth();
16998 var startingPos = firstOfMonth.getDay()-this.startDay;
17000 if(startingPos < this.startDay){
17004 var pm = date.add(Date.MONTH, -1);
17005 var prevStart = pm.getDaysInMonth()-startingPos;
17007 this.cells = this.el.select('.fc-day',true);
17008 this.textNodes = this.el.query('.fc-day-number');
17009 this.cells.addClassOnOver('fc-state-hover');
17011 var cells = this.cells.elements;
17012 var textEls = this.textNodes;
17014 Roo.each(cells, function(cell){
17015 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17018 days += startingPos;
17020 // convert everything to numbers so it's fast
17021 var day = 86400000;
17022 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17025 //Roo.log(prevStart);
17027 var today = new Date().clearTime().getTime();
17028 var sel = date.clearTime().getTime();
17029 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17030 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17031 var ddMatch = this.disabledDatesRE;
17032 var ddText = this.disabledDatesText;
17033 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17034 var ddaysText = this.disabledDaysText;
17035 var format = this.format;
17037 var setCellClass = function(cal, cell){
17041 //Roo.log('set Cell Class');
17043 var t = d.getTime();
17047 cell.dateValue = t;
17049 cell.className += " fc-today";
17050 cell.className += " fc-state-highlight";
17051 cell.title = cal.todayText;
17054 // disable highlight in other month..
17055 //cell.className += " fc-state-highlight";
17060 cell.className = " fc-state-disabled";
17061 cell.title = cal.minText;
17065 cell.className = " fc-state-disabled";
17066 cell.title = cal.maxText;
17070 if(ddays.indexOf(d.getDay()) != -1){
17071 cell.title = ddaysText;
17072 cell.className = " fc-state-disabled";
17075 if(ddMatch && format){
17076 var fvalue = d.dateFormat(format);
17077 if(ddMatch.test(fvalue)){
17078 cell.title = ddText.replace("%0", fvalue);
17079 cell.className = " fc-state-disabled";
17083 if (!cell.initialClassName) {
17084 cell.initialClassName = cell.dom.className;
17087 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17092 for(; i < startingPos; i++) {
17093 textEls[i].innerHTML = (++prevStart);
17094 d.setDate(d.getDate()+1);
17096 cells[i].className = "fc-past fc-other-month";
17097 setCellClass(this, cells[i]);
17102 for(; i < days; i++){
17103 intDay = i - startingPos + 1;
17104 textEls[i].innerHTML = (intDay);
17105 d.setDate(d.getDate()+1);
17107 cells[i].className = ''; // "x-date-active";
17108 setCellClass(this, cells[i]);
17112 for(; i < 42; i++) {
17113 textEls[i].innerHTML = (++extraDays);
17114 d.setDate(d.getDate()+1);
17116 cells[i].className = "fc-future fc-other-month";
17117 setCellClass(this, cells[i]);
17120 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17122 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17124 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17125 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17127 if(totalRows != 6){
17128 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17129 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17132 this.fireEvent('monthchange', this, date);
17136 if(!this.internalRender){
17137 var main = this.el.dom.firstChild;
17138 var w = main.offsetWidth;
17139 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17140 Roo.fly(main).setWidth(w);
17141 this.internalRender = true;
17142 // opera does not respect the auto grow header center column
17143 // then, after it gets a width opera refuses to recalculate
17144 // without a second pass
17145 if(Roo.isOpera && !this.secondPass){
17146 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17147 this.secondPass = true;
17148 this.update.defer(10, this, [date]);
17155 findCell : function(dt) {
17156 dt = dt.clearTime().getTime();
17158 this.cells.each(function(c){
17159 //Roo.log("check " +c.dateValue + '?=' + dt);
17160 if(c.dateValue == dt){
17170 findCells : function(ev) {
17171 var s = ev.start.clone().clearTime().getTime();
17173 var e= ev.end.clone().clearTime().getTime();
17176 this.cells.each(function(c){
17177 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17179 if(c.dateValue > e){
17182 if(c.dateValue < s){
17191 // findBestRow: function(cells)
17195 // for (var i =0 ; i < cells.length;i++) {
17196 // ret = Math.max(cells[i].rows || 0,ret);
17203 addItem : function(ev)
17205 // look for vertical location slot in
17206 var cells = this.findCells(ev);
17208 // ev.row = this.findBestRow(cells);
17210 // work out the location.
17214 for(var i =0; i < cells.length; i++) {
17216 cells[i].row = cells[0].row;
17219 cells[i].row = cells[i].row + 1;
17229 if (crow.start.getY() == cells[i].getY()) {
17231 crow.end = cells[i];
17248 cells[0].events.push(ev);
17250 this.calevents.push(ev);
17253 clearEvents: function() {
17255 if(!this.calevents){
17259 Roo.each(this.cells.elements, function(c){
17265 Roo.each(this.calevents, function(e) {
17266 Roo.each(e.els, function(el) {
17267 el.un('mouseenter' ,this.onEventEnter, this);
17268 el.un('mouseleave' ,this.onEventLeave, this);
17273 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17279 renderEvents: function()
17283 this.cells.each(function(c) {
17292 if(c.row != c.events.length){
17293 r = 4 - (4 - (c.row - c.events.length));
17296 c.events = ev.slice(0, r);
17297 c.more = ev.slice(r);
17299 if(c.more.length && c.more.length == 1){
17300 c.events.push(c.more.pop());
17303 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17307 this.cells.each(function(c) {
17309 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17312 for (var e = 0; e < c.events.length; e++){
17313 var ev = c.events[e];
17314 var rows = ev.rows;
17316 for(var i = 0; i < rows.length; i++) {
17318 // how many rows should it span..
17321 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17322 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17324 unselectable : "on",
17327 cls: 'fc-event-inner',
17331 // cls: 'fc-event-time',
17332 // html : cells.length > 1 ? '' : ev.time
17336 cls: 'fc-event-title',
17337 html : String.format('{0}', ev.title)
17344 cls: 'ui-resizable-handle ui-resizable-e',
17345 html : '  '
17352 cfg.cls += ' fc-event-start';
17354 if ((i+1) == rows.length) {
17355 cfg.cls += ' fc-event-end';
17358 var ctr = _this.el.select('.fc-event-container',true).first();
17359 var cg = ctr.createChild(cfg);
17361 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17362 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17364 var r = (c.more.length) ? 1 : 0;
17365 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17366 cg.setWidth(ebox.right - sbox.x -2);
17368 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17369 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17370 cg.on('click', _this.onEventClick, _this, ev);
17381 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17382 style : 'position: absolute',
17383 unselectable : "on",
17386 cls: 'fc-event-inner',
17390 cls: 'fc-event-title',
17398 cls: 'ui-resizable-handle ui-resizable-e',
17399 html : '  '
17405 var ctr = _this.el.select('.fc-event-container',true).first();
17406 var cg = ctr.createChild(cfg);
17408 var sbox = c.select('.fc-day-content',true).first().getBox();
17409 var ebox = c.select('.fc-day-content',true).first().getBox();
17411 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17412 cg.setWidth(ebox.right - sbox.x -2);
17414 cg.on('click', _this.onMoreEventClick, _this, c.more);
17424 onEventEnter: function (e, el,event,d) {
17425 this.fireEvent('evententer', this, el, event);
17428 onEventLeave: function (e, el,event,d) {
17429 this.fireEvent('eventleave', this, el, event);
17432 onEventClick: function (e, el,event,d) {
17433 this.fireEvent('eventclick', this, el, event);
17436 onMonthChange: function () {
17440 onMoreEventClick: function(e, el, more)
17444 this.calpopover.placement = 'right';
17445 this.calpopover.setTitle('More');
17447 this.calpopover.setContent('');
17449 var ctr = this.calpopover.el.select('.popover-content', true).first();
17451 Roo.each(more, function(m){
17453 cls : 'fc-event-hori fc-event-draggable',
17456 var cg = ctr.createChild(cfg);
17458 cg.on('click', _this.onEventClick, _this, m);
17461 this.calpopover.show(el);
17466 onLoad: function ()
17468 this.calevents = [];
17471 if(this.store.getCount() > 0){
17472 this.store.data.each(function(d){
17475 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17476 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17477 time : d.data.start_time,
17478 title : d.data.title,
17479 description : d.data.description,
17480 venue : d.data.venue
17485 this.renderEvents();
17487 if(this.calevents.length && this.loadMask){
17488 this.maskEl.hide();
17492 onBeforeLoad: function()
17494 this.clearEvents();
17496 this.maskEl.show();
17510 * @class Roo.bootstrap.Popover
17511 * @extends Roo.bootstrap.Component
17512 * Bootstrap Popover class
17513 * @cfg {String} html contents of the popover (or false to use children..)
17514 * @cfg {String} title of popover (or false to hide)
17515 * @cfg {String} placement how it is placed
17516 * @cfg {String} trigger click || hover (or false to trigger manually)
17517 * @cfg {String} over what (parent or false to trigger manually.)
17518 * @cfg {Number} delay - delay before showing
17521 * Create a new Popover
17522 * @param {Object} config The config object
17525 Roo.bootstrap.Popover = function(config){
17526 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17532 * After the popover show
17534 * @param {Roo.bootstrap.Popover} this
17539 * After the popover hide
17541 * @param {Roo.bootstrap.Popover} this
17547 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17549 title: 'Fill in a title',
17552 placement : 'right',
17553 trigger : 'hover', // hover
17559 can_build_overlaid : false,
17561 getChildContainer : function()
17563 return this.el.select('.popover-content',true).first();
17566 getAutoCreate : function(){
17569 cls : 'popover roo-dynamic',
17570 style: 'display:block',
17576 cls : 'popover-inner',
17580 cls: 'popover-title',
17584 cls : 'popover-content',
17595 setTitle: function(str)
17598 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17600 setContent: function(str)
17603 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17605 // as it get's added to the bottom of the page.
17606 onRender : function(ct, position)
17608 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17610 var cfg = Roo.apply({}, this.getAutoCreate());
17614 cfg.cls += ' ' + this.cls;
17617 cfg.style = this.style;
17619 //Roo.log("adding to ");
17620 this.el = Roo.get(document.body).createChild(cfg, position);
17621 // Roo.log(this.el);
17626 initEvents : function()
17628 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17629 this.el.enableDisplayMode('block');
17631 if (this.over === false) {
17634 if (this.triggers === false) {
17637 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17638 var triggers = this.trigger ? this.trigger.split(' ') : [];
17639 Roo.each(triggers, function(trigger) {
17641 if (trigger == 'click') {
17642 on_el.on('click', this.toggle, this);
17643 } else if (trigger != 'manual') {
17644 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17645 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17647 on_el.on(eventIn ,this.enter, this);
17648 on_el.on(eventOut, this.leave, this);
17659 toggle : function () {
17660 this.hoverState == 'in' ? this.leave() : this.enter();
17663 enter : function () {
17665 clearTimeout(this.timeout);
17667 this.hoverState = 'in';
17669 if (!this.delay || !this.delay.show) {
17674 this.timeout = setTimeout(function () {
17675 if (_t.hoverState == 'in') {
17678 }, this.delay.show)
17681 leave : function() {
17682 clearTimeout(this.timeout);
17684 this.hoverState = 'out';
17686 if (!this.delay || !this.delay.hide) {
17691 this.timeout = setTimeout(function () {
17692 if (_t.hoverState == 'out') {
17695 }, this.delay.hide)
17698 show : function (on_el)
17701 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17705 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17706 if (this.html !== false) {
17707 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17709 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17710 if (!this.title.length) {
17711 this.el.select('.popover-title',true).hide();
17714 var placement = typeof this.placement == 'function' ?
17715 this.placement.call(this, this.el, on_el) :
17718 var autoToken = /\s?auto?\s?/i;
17719 var autoPlace = autoToken.test(placement);
17721 placement = placement.replace(autoToken, '') || 'top';
17725 //this.el.setXY([0,0]);
17727 this.el.dom.style.display='block';
17728 this.el.addClass(placement);
17730 //this.el.appendTo(on_el);
17732 var p = this.getPosition();
17733 var box = this.el.getBox();
17738 var align = Roo.bootstrap.Popover.alignment[placement];
17741 this.el.alignTo(on_el, align[0],align[1]);
17742 //var arrow = this.el.select('.arrow',true).first();
17743 //arrow.set(align[2],
17745 this.el.addClass('in');
17748 if (this.el.hasClass('fade')) {
17752 this.hoverState = 'in';
17754 this.fireEvent('show', this);
17759 this.el.setXY([0,0]);
17760 this.el.removeClass('in');
17762 this.hoverState = null;
17764 this.fireEvent('hide', this);
17769 Roo.bootstrap.Popover.alignment = {
17770 'left' : ['r-l', [-10,0], 'right'],
17771 'right' : ['l-r', [10,0], 'left'],
17772 'bottom' : ['t-b', [0,10], 'top'],
17773 'top' : [ 'b-t', [0,-10], 'bottom']
17784 * @class Roo.bootstrap.Progress
17785 * @extends Roo.bootstrap.Component
17786 * Bootstrap Progress class
17787 * @cfg {Boolean} striped striped of the progress bar
17788 * @cfg {Boolean} active animated of the progress bar
17792 * Create a new Progress
17793 * @param {Object} config The config object
17796 Roo.bootstrap.Progress = function(config){
17797 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17800 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17805 getAutoCreate : function(){
17813 cfg.cls += ' progress-striped';
17817 cfg.cls += ' active';
17836 * @class Roo.bootstrap.ProgressBar
17837 * @extends Roo.bootstrap.Component
17838 * Bootstrap ProgressBar class
17839 * @cfg {Number} aria_valuenow aria-value now
17840 * @cfg {Number} aria_valuemin aria-value min
17841 * @cfg {Number} aria_valuemax aria-value max
17842 * @cfg {String} label label for the progress bar
17843 * @cfg {String} panel (success | info | warning | danger )
17844 * @cfg {String} role role of the progress bar
17845 * @cfg {String} sr_only text
17849 * Create a new ProgressBar
17850 * @param {Object} config The config object
17853 Roo.bootstrap.ProgressBar = function(config){
17854 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17857 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17861 aria_valuemax : 100,
17867 getAutoCreate : function()
17872 cls: 'progress-bar',
17873 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17885 cfg.role = this.role;
17888 if(this.aria_valuenow){
17889 cfg['aria-valuenow'] = this.aria_valuenow;
17892 if(this.aria_valuemin){
17893 cfg['aria-valuemin'] = this.aria_valuemin;
17896 if(this.aria_valuemax){
17897 cfg['aria-valuemax'] = this.aria_valuemax;
17900 if(this.label && !this.sr_only){
17901 cfg.html = this.label;
17905 cfg.cls += ' progress-bar-' + this.panel;
17911 update : function(aria_valuenow)
17913 this.aria_valuenow = aria_valuenow;
17915 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17930 * @class Roo.bootstrap.TabGroup
17931 * @extends Roo.bootstrap.Column
17932 * Bootstrap Column class
17933 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17934 * @cfg {Boolean} carousel true to make the group behave like a carousel
17935 * @cfg {Boolean} bullets show bullets for the panels
17936 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17937 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17938 * @cfg {Boolean} showarrow (true|false) show arrow default true
17941 * Create a new TabGroup
17942 * @param {Object} config The config object
17945 Roo.bootstrap.TabGroup = function(config){
17946 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17948 this.navId = Roo.id();
17951 Roo.bootstrap.TabGroup.register(this);
17955 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17958 transition : false,
17963 slideOnTouch : false,
17966 getAutoCreate : function()
17968 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17970 cfg.cls += ' tab-content';
17972 if (this.carousel) {
17973 cfg.cls += ' carousel slide';
17976 cls : 'carousel-inner',
17980 if(this.bullets && !Roo.isTouch){
17983 cls : 'carousel-bullets',
17987 if(this.bullets_cls){
17988 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17995 cfg.cn[0].cn.push(bullets);
17998 if(this.showarrow){
17999 cfg.cn[0].cn.push({
18001 class : 'carousel-arrow',
18005 class : 'carousel-prev',
18009 class : 'fa fa-chevron-left'
18015 class : 'carousel-next',
18019 class : 'fa fa-chevron-right'
18032 initEvents: function()
18034 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18035 // this.el.on("touchstart", this.onTouchStart, this);
18038 if(this.autoslide){
18041 this.slideFn = window.setInterval(function() {
18042 _this.showPanelNext();
18046 if(this.showarrow){
18047 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18048 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18054 // onTouchStart : function(e, el, o)
18056 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18060 // this.showPanelNext();
18064 getChildContainer : function()
18066 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18070 * register a Navigation item
18071 * @param {Roo.bootstrap.NavItem} the navitem to add
18073 register : function(item)
18075 this.tabs.push( item);
18076 item.navId = this.navId; // not really needed..
18081 getActivePanel : function()
18084 Roo.each(this.tabs, function(t) {
18094 getPanelByName : function(n)
18097 Roo.each(this.tabs, function(t) {
18098 if (t.tabId == n) {
18106 indexOfPanel : function(p)
18109 Roo.each(this.tabs, function(t,i) {
18110 if (t.tabId == p.tabId) {
18119 * show a specific panel
18120 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18121 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18123 showPanel : function (pan)
18125 if(this.transition || typeof(pan) == 'undefined'){
18126 Roo.log("waiting for the transitionend");
18130 if (typeof(pan) == 'number') {
18131 pan = this.tabs[pan];
18134 if (typeof(pan) == 'string') {
18135 pan = this.getPanelByName(pan);
18138 var cur = this.getActivePanel();
18141 Roo.log('pan or acitve pan is undefined');
18145 if (pan.tabId == this.getActivePanel().tabId) {
18149 if (false === cur.fireEvent('beforedeactivate')) {
18153 if(this.bullets > 0 && !Roo.isTouch){
18154 this.setActiveBullet(this.indexOfPanel(pan));
18157 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18159 this.transition = true;
18160 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18161 var lr = dir == 'next' ? 'left' : 'right';
18162 pan.el.addClass(dir); // or prev
18163 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18164 cur.el.addClass(lr); // or right
18165 pan.el.addClass(lr);
18168 cur.el.on('transitionend', function() {
18169 Roo.log("trans end?");
18171 pan.el.removeClass([lr,dir]);
18172 pan.setActive(true);
18174 cur.el.removeClass([lr]);
18175 cur.setActive(false);
18177 _this.transition = false;
18179 }, this, { single: true } );
18184 cur.setActive(false);
18185 pan.setActive(true);
18190 showPanelNext : function()
18192 var i = this.indexOfPanel(this.getActivePanel());
18194 if (i >= this.tabs.length - 1 && !this.autoslide) {
18198 if (i >= this.tabs.length - 1 && this.autoslide) {
18202 this.showPanel(this.tabs[i+1]);
18205 showPanelPrev : function()
18207 var i = this.indexOfPanel(this.getActivePanel());
18209 if (i < 1 && !this.autoslide) {
18213 if (i < 1 && this.autoslide) {
18214 i = this.tabs.length;
18217 this.showPanel(this.tabs[i-1]);
18221 addBullet: function()
18223 if(!this.bullets || Roo.isTouch){
18226 var ctr = this.el.select('.carousel-bullets',true).first();
18227 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18228 var bullet = ctr.createChild({
18229 cls : 'bullet bullet-' + i
18230 },ctr.dom.lastChild);
18235 bullet.on('click', (function(e, el, o, ii, t){
18237 e.preventDefault();
18239 this.showPanel(ii);
18241 if(this.autoslide && this.slideFn){
18242 clearInterval(this.slideFn);
18243 this.slideFn = window.setInterval(function() {
18244 _this.showPanelNext();
18248 }).createDelegate(this, [i, bullet], true));
18253 setActiveBullet : function(i)
18259 Roo.each(this.el.select('.bullet', true).elements, function(el){
18260 el.removeClass('selected');
18263 var bullet = this.el.select('.bullet-' + i, true).first();
18269 bullet.addClass('selected');
18280 Roo.apply(Roo.bootstrap.TabGroup, {
18284 * register a Navigation Group
18285 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18287 register : function(navgrp)
18289 this.groups[navgrp.navId] = navgrp;
18293 * fetch a Navigation Group based on the navigation ID
18294 * if one does not exist , it will get created.
18295 * @param {string} the navgroup to add
18296 * @returns {Roo.bootstrap.NavGroup} the navgroup
18298 get: function(navId) {
18299 if (typeof(this.groups[navId]) == 'undefined') {
18300 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18302 return this.groups[navId] ;
18317 * @class Roo.bootstrap.TabPanel
18318 * @extends Roo.bootstrap.Component
18319 * Bootstrap TabPanel class
18320 * @cfg {Boolean} active panel active
18321 * @cfg {String} html panel content
18322 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18323 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18324 * @cfg {String} href click to link..
18328 * Create a new TabPanel
18329 * @param {Object} config The config object
18332 Roo.bootstrap.TabPanel = function(config){
18333 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18337 * Fires when the active status changes
18338 * @param {Roo.bootstrap.TabPanel} this
18339 * @param {Boolean} state the new state
18344 * @event beforedeactivate
18345 * Fires before a tab is de-activated - can be used to do validation on a form.
18346 * @param {Roo.bootstrap.TabPanel} this
18347 * @return {Boolean} false if there is an error
18350 'beforedeactivate': true
18353 this.tabId = this.tabId || Roo.id();
18357 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18365 getAutoCreate : function(){
18368 // item is needed for carousel - not sure if it has any effect otherwise
18369 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18370 html: this.html || ''
18374 cfg.cls += ' active';
18378 cfg.tabId = this.tabId;
18385 initEvents: function()
18387 var p = this.parent();
18389 this.navId = this.navId || p.navId;
18391 if (typeof(this.navId) != 'undefined') {
18392 // not really needed.. but just in case.. parent should be a NavGroup.
18393 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18397 var i = tg.tabs.length - 1;
18399 if(this.active && tg.bullets > 0 && i < tg.bullets){
18400 tg.setActiveBullet(i);
18404 this.el.on('click', this.onClick, this);
18407 this.el.on("touchstart", this.onTouchStart, this);
18408 this.el.on("touchmove", this.onTouchMove, this);
18409 this.el.on("touchend", this.onTouchEnd, this);
18414 onRender : function(ct, position)
18416 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18419 setActive : function(state)
18421 Roo.log("panel - set active " + this.tabId + "=" + state);
18423 this.active = state;
18425 this.el.removeClass('active');
18427 } else if (!this.el.hasClass('active')) {
18428 this.el.addClass('active');
18431 this.fireEvent('changed', this, state);
18434 onClick : function(e)
18436 e.preventDefault();
18438 if(!this.href.length){
18442 window.location.href = this.href;
18451 onTouchStart : function(e)
18453 this.swiping = false;
18455 this.startX = e.browserEvent.touches[0].clientX;
18456 this.startY = e.browserEvent.touches[0].clientY;
18459 onTouchMove : function(e)
18461 this.swiping = true;
18463 this.endX = e.browserEvent.touches[0].clientX;
18464 this.endY = e.browserEvent.touches[0].clientY;
18467 onTouchEnd : function(e)
18474 var tabGroup = this.parent();
18476 if(this.endX > this.startX){ // swiping right
18477 tabGroup.showPanelPrev();
18481 if(this.startX > this.endX){ // swiping left
18482 tabGroup.showPanelNext();
18501 * @class Roo.bootstrap.DateField
18502 * @extends Roo.bootstrap.Input
18503 * Bootstrap DateField class
18504 * @cfg {Number} weekStart default 0
18505 * @cfg {String} viewMode default empty, (months|years)
18506 * @cfg {String} minViewMode default empty, (months|years)
18507 * @cfg {Number} startDate default -Infinity
18508 * @cfg {Number} endDate default Infinity
18509 * @cfg {Boolean} todayHighlight default false
18510 * @cfg {Boolean} todayBtn default false
18511 * @cfg {Boolean} calendarWeeks default false
18512 * @cfg {Object} daysOfWeekDisabled default empty
18513 * @cfg {Boolean} singleMode default false (true | false)
18515 * @cfg {Boolean} keyboardNavigation default true
18516 * @cfg {String} language default en
18519 * Create a new DateField
18520 * @param {Object} config The config object
18523 Roo.bootstrap.DateField = function(config){
18524 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18528 * Fires when this field show.
18529 * @param {Roo.bootstrap.DateField} this
18530 * @param {Mixed} date The date value
18535 * Fires when this field hide.
18536 * @param {Roo.bootstrap.DateField} this
18537 * @param {Mixed} date The date value
18542 * Fires when select a date.
18543 * @param {Roo.bootstrap.DateField} this
18544 * @param {Mixed} date The date value
18548 * @event beforeselect
18549 * Fires when before select a date.
18550 * @param {Roo.bootstrap.DateField} this
18551 * @param {Mixed} date The date value
18553 beforeselect : true
18557 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18560 * @cfg {String} format
18561 * The default date format string which can be overriden for localization support. The format must be
18562 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18566 * @cfg {String} altFormats
18567 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18568 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18570 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18578 todayHighlight : false,
18584 keyboardNavigation: true,
18586 calendarWeeks: false,
18588 startDate: -Infinity,
18592 daysOfWeekDisabled: [],
18596 singleMode : false,
18598 UTCDate: function()
18600 return new Date(Date.UTC.apply(Date, arguments));
18603 UTCToday: function()
18605 var today = new Date();
18606 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18609 getDate: function() {
18610 var d = this.getUTCDate();
18611 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18614 getUTCDate: function() {
18618 setDate: function(d) {
18619 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18622 setUTCDate: function(d) {
18624 this.setValue(this.formatDate(this.date));
18627 onRender: function(ct, position)
18630 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18632 this.language = this.language || 'en';
18633 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18634 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18636 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18637 this.format = this.format || 'm/d/y';
18638 this.isInline = false;
18639 this.isInput = true;
18640 this.component = this.el.select('.add-on', true).first() || false;
18641 this.component = (this.component && this.component.length === 0) ? false : this.component;
18642 this.hasInput = this.component && this.inputEl().length;
18644 if (typeof(this.minViewMode === 'string')) {
18645 switch (this.minViewMode) {
18647 this.minViewMode = 1;
18650 this.minViewMode = 2;
18653 this.minViewMode = 0;
18658 if (typeof(this.viewMode === 'string')) {
18659 switch (this.viewMode) {
18672 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18674 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18676 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18678 this.picker().on('mousedown', this.onMousedown, this);
18679 this.picker().on('click', this.onClick, this);
18681 this.picker().addClass('datepicker-dropdown');
18683 this.startViewMode = this.viewMode;
18685 if(this.singleMode){
18686 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18687 v.setVisibilityMode(Roo.Element.DISPLAY);
18691 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18692 v.setStyle('width', '189px');
18696 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18697 if(!this.calendarWeeks){
18702 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18703 v.attr('colspan', function(i, val){
18704 return parseInt(val) + 1;
18709 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18711 this.setStartDate(this.startDate);
18712 this.setEndDate(this.endDate);
18714 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18721 if(this.isInline) {
18726 picker : function()
18728 return this.pickerEl;
18729 // return this.el.select('.datepicker', true).first();
18732 fillDow: function()
18734 var dowCnt = this.weekStart;
18743 if(this.calendarWeeks){
18751 while (dowCnt < this.weekStart + 7) {
18755 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18759 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18762 fillMonths: function()
18765 var months = this.picker().select('>.datepicker-months td', true).first();
18767 months.dom.innerHTML = '';
18773 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18776 months.createChild(month);
18783 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;
18785 if (this.date < this.startDate) {
18786 this.viewDate = new Date(this.startDate);
18787 } else if (this.date > this.endDate) {
18788 this.viewDate = new Date(this.endDate);
18790 this.viewDate = new Date(this.date);
18798 var d = new Date(this.viewDate),
18799 year = d.getUTCFullYear(),
18800 month = d.getUTCMonth(),
18801 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18802 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18803 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18804 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18805 currentDate = this.date && this.date.valueOf(),
18806 today = this.UTCToday();
18808 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18810 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18812 // this.picker.select('>tfoot th.today').
18813 // .text(dates[this.language].today)
18814 // .toggle(this.todayBtn !== false);
18816 this.updateNavArrows();
18819 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18821 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18823 prevMonth.setUTCDate(day);
18825 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18827 var nextMonth = new Date(prevMonth);
18829 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18831 nextMonth = nextMonth.valueOf();
18833 var fillMonths = false;
18835 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18837 while(prevMonth.valueOf() <= nextMonth) {
18840 if (prevMonth.getUTCDay() === this.weekStart) {
18842 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18850 if(this.calendarWeeks){
18851 // ISO 8601: First week contains first thursday.
18852 // ISO also states week starts on Monday, but we can be more abstract here.
18854 // Start of current week: based on weekstart/current date
18855 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18856 // Thursday of this week
18857 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18858 // First Thursday of year, year from thursday
18859 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18860 // Calendar week: ms between thursdays, div ms per day, div 7 days
18861 calWeek = (th - yth) / 864e5 / 7 + 1;
18863 fillMonths.cn.push({
18871 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18873 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18876 if (this.todayHighlight &&
18877 prevMonth.getUTCFullYear() == today.getFullYear() &&
18878 prevMonth.getUTCMonth() == today.getMonth() &&
18879 prevMonth.getUTCDate() == today.getDate()) {
18880 clsName += ' today';
18883 if (currentDate && prevMonth.valueOf() === currentDate) {
18884 clsName += ' active';
18887 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18888 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18889 clsName += ' disabled';
18892 fillMonths.cn.push({
18894 cls: 'day ' + clsName,
18895 html: prevMonth.getDate()
18898 prevMonth.setDate(prevMonth.getDate()+1);
18901 var currentYear = this.date && this.date.getUTCFullYear();
18902 var currentMonth = this.date && this.date.getUTCMonth();
18904 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18906 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18907 v.removeClass('active');
18909 if(currentYear === year && k === currentMonth){
18910 v.addClass('active');
18913 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18914 v.addClass('disabled');
18920 year = parseInt(year/10, 10) * 10;
18922 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18924 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18927 for (var i = -1; i < 11; i++) {
18928 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18930 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18938 showMode: function(dir)
18941 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18944 Roo.each(this.picker().select('>div',true).elements, function(v){
18945 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18948 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18953 if(this.isInline) {
18957 this.picker().removeClass(['bottom', 'top']);
18959 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18961 * place to the top of element!
18965 this.picker().addClass('top');
18966 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18971 this.picker().addClass('bottom');
18973 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18976 parseDate : function(value)
18978 if(!value || value instanceof Date){
18981 var v = Date.parseDate(value, this.format);
18982 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18983 v = Date.parseDate(value, 'Y-m-d');
18985 if(!v && this.altFormats){
18986 if(!this.altFormatsArray){
18987 this.altFormatsArray = this.altFormats.split("|");
18989 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18990 v = Date.parseDate(value, this.altFormatsArray[i]);
18996 formatDate : function(date, fmt)
18998 return (!date || !(date instanceof Date)) ?
18999 date : date.dateFormat(fmt || this.format);
19002 onFocus : function()
19004 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19008 onBlur : function()
19010 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19012 var d = this.inputEl().getValue();
19019 showPopup : function()
19021 this.picker().show();
19025 this.fireEvent('showpopup', this, this.date);
19028 hidePopup : function()
19030 if(this.isInline) {
19033 this.picker().hide();
19034 this.viewMode = this.startViewMode;
19037 this.fireEvent('hidepopup', this, this.date);
19041 onMousedown: function(e)
19043 e.stopPropagation();
19044 e.preventDefault();
19049 Roo.bootstrap.DateField.superclass.keyup.call(this);
19053 setValue: function(v)
19055 if(this.fireEvent('beforeselect', this, v) !== false){
19056 var d = new Date(this.parseDate(v) ).clearTime();
19058 if(isNaN(d.getTime())){
19059 this.date = this.viewDate = '';
19060 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19064 v = this.formatDate(d);
19066 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19068 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19072 this.fireEvent('select', this, this.date);
19076 getValue: function()
19078 return this.formatDate(this.date);
19081 fireKey: function(e)
19083 if (!this.picker().isVisible()){
19084 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19090 var dateChanged = false,
19092 newDate, newViewDate;
19097 e.preventDefault();
19101 if (!this.keyboardNavigation) {
19104 dir = e.keyCode == 37 ? -1 : 1;
19107 newDate = this.moveYear(this.date, dir);
19108 newViewDate = this.moveYear(this.viewDate, dir);
19109 } else if (e.shiftKey){
19110 newDate = this.moveMonth(this.date, dir);
19111 newViewDate = this.moveMonth(this.viewDate, dir);
19113 newDate = new Date(this.date);
19114 newDate.setUTCDate(this.date.getUTCDate() + dir);
19115 newViewDate = new Date(this.viewDate);
19116 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19118 if (this.dateWithinRange(newDate)){
19119 this.date = newDate;
19120 this.viewDate = newViewDate;
19121 this.setValue(this.formatDate(this.date));
19123 e.preventDefault();
19124 dateChanged = true;
19129 if (!this.keyboardNavigation) {
19132 dir = e.keyCode == 38 ? -1 : 1;
19134 newDate = this.moveYear(this.date, dir);
19135 newViewDate = this.moveYear(this.viewDate, dir);
19136 } else if (e.shiftKey){
19137 newDate = this.moveMonth(this.date, dir);
19138 newViewDate = this.moveMonth(this.viewDate, dir);
19140 newDate = new Date(this.date);
19141 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19142 newViewDate = new Date(this.viewDate);
19143 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19145 if (this.dateWithinRange(newDate)){
19146 this.date = newDate;
19147 this.viewDate = newViewDate;
19148 this.setValue(this.formatDate(this.date));
19150 e.preventDefault();
19151 dateChanged = true;
19155 this.setValue(this.formatDate(this.date));
19157 e.preventDefault();
19160 this.setValue(this.formatDate(this.date));
19174 onClick: function(e)
19176 e.stopPropagation();
19177 e.preventDefault();
19179 var target = e.getTarget();
19181 if(target.nodeName.toLowerCase() === 'i'){
19182 target = Roo.get(target).dom.parentNode;
19185 var nodeName = target.nodeName;
19186 var className = target.className;
19187 var html = target.innerHTML;
19188 //Roo.log(nodeName);
19190 switch(nodeName.toLowerCase()) {
19192 switch(className) {
19198 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19199 switch(this.viewMode){
19201 this.viewDate = this.moveMonth(this.viewDate, dir);
19205 this.viewDate = this.moveYear(this.viewDate, dir);
19211 var date = new Date();
19212 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19214 this.setValue(this.formatDate(this.date));
19221 if (className.indexOf('disabled') < 0) {
19222 this.viewDate.setUTCDate(1);
19223 if (className.indexOf('month') > -1) {
19224 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19226 var year = parseInt(html, 10) || 0;
19227 this.viewDate.setUTCFullYear(year);
19231 if(this.singleMode){
19232 this.setValue(this.formatDate(this.viewDate));
19243 //Roo.log(className);
19244 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19245 var day = parseInt(html, 10) || 1;
19246 var year = this.viewDate.getUTCFullYear(),
19247 month = this.viewDate.getUTCMonth();
19249 if (className.indexOf('old') > -1) {
19256 } else if (className.indexOf('new') > -1) {
19264 //Roo.log([year,month,day]);
19265 this.date = this.UTCDate(year, month, day,0,0,0,0);
19266 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19268 //Roo.log(this.formatDate(this.date));
19269 this.setValue(this.formatDate(this.date));
19276 setStartDate: function(startDate)
19278 this.startDate = startDate || -Infinity;
19279 if (this.startDate !== -Infinity) {
19280 this.startDate = this.parseDate(this.startDate);
19283 this.updateNavArrows();
19286 setEndDate: function(endDate)
19288 this.endDate = endDate || Infinity;
19289 if (this.endDate !== Infinity) {
19290 this.endDate = this.parseDate(this.endDate);
19293 this.updateNavArrows();
19296 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19298 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19299 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19300 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19302 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19303 return parseInt(d, 10);
19306 this.updateNavArrows();
19309 updateNavArrows: function()
19311 if(this.singleMode){
19315 var d = new Date(this.viewDate),
19316 year = d.getUTCFullYear(),
19317 month = d.getUTCMonth();
19319 Roo.each(this.picker().select('.prev', true).elements, function(v){
19321 switch (this.viewMode) {
19324 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19330 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19337 Roo.each(this.picker().select('.next', true).elements, function(v){
19339 switch (this.viewMode) {
19342 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19348 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19356 moveMonth: function(date, dir)
19361 var new_date = new Date(date.valueOf()),
19362 day = new_date.getUTCDate(),
19363 month = new_date.getUTCMonth(),
19364 mag = Math.abs(dir),
19366 dir = dir > 0 ? 1 : -1;
19369 // If going back one month, make sure month is not current month
19370 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19372 return new_date.getUTCMonth() == month;
19374 // If going forward one month, make sure month is as expected
19375 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19377 return new_date.getUTCMonth() != new_month;
19379 new_month = month + dir;
19380 new_date.setUTCMonth(new_month);
19381 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19382 if (new_month < 0 || new_month > 11) {
19383 new_month = (new_month + 12) % 12;
19386 // For magnitudes >1, move one month at a time...
19387 for (var i=0; i<mag; i++) {
19388 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19389 new_date = this.moveMonth(new_date, dir);
19391 // ...then reset the day, keeping it in the new month
19392 new_month = new_date.getUTCMonth();
19393 new_date.setUTCDate(day);
19395 return new_month != new_date.getUTCMonth();
19398 // Common date-resetting loop -- if date is beyond end of month, make it
19401 new_date.setUTCDate(--day);
19402 new_date.setUTCMonth(new_month);
19407 moveYear: function(date, dir)
19409 return this.moveMonth(date, dir*12);
19412 dateWithinRange: function(date)
19414 return date >= this.startDate && date <= this.endDate;
19420 this.picker().remove();
19423 validateValue : function(value)
19425 if(this.getVisibilityEl().hasClass('hidden')){
19429 if(value.length < 1) {
19430 if(this.allowBlank){
19436 if(value.length < this.minLength){
19439 if(value.length > this.maxLength){
19443 var vt = Roo.form.VTypes;
19444 if(!vt[this.vtype](value, this)){
19448 if(typeof this.validator == "function"){
19449 var msg = this.validator(value);
19455 if(this.regex && !this.regex.test(value)){
19459 if(typeof(this.parseDate(value)) == 'undefined'){
19463 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19467 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19477 this.date = this.viewDate = '';
19479 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19484 Roo.apply(Roo.bootstrap.DateField, {
19495 html: '<i class="fa fa-arrow-left"/>'
19505 html: '<i class="fa fa-arrow-right"/>'
19547 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19548 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19549 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19550 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19551 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19564 navFnc: 'FullYear',
19569 navFnc: 'FullYear',
19574 Roo.apply(Roo.bootstrap.DateField, {
19578 cls: 'datepicker dropdown-menu roo-dynamic',
19582 cls: 'datepicker-days',
19586 cls: 'table-condensed',
19588 Roo.bootstrap.DateField.head,
19592 Roo.bootstrap.DateField.footer
19599 cls: 'datepicker-months',
19603 cls: 'table-condensed',
19605 Roo.bootstrap.DateField.head,
19606 Roo.bootstrap.DateField.content,
19607 Roo.bootstrap.DateField.footer
19614 cls: 'datepicker-years',
19618 cls: 'table-condensed',
19620 Roo.bootstrap.DateField.head,
19621 Roo.bootstrap.DateField.content,
19622 Roo.bootstrap.DateField.footer
19641 * @class Roo.bootstrap.TimeField
19642 * @extends Roo.bootstrap.Input
19643 * Bootstrap DateField class
19647 * Create a new TimeField
19648 * @param {Object} config The config object
19651 Roo.bootstrap.TimeField = function(config){
19652 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19656 * Fires when this field show.
19657 * @param {Roo.bootstrap.DateField} thisthis
19658 * @param {Mixed} date The date value
19663 * Fires when this field hide.
19664 * @param {Roo.bootstrap.DateField} this
19665 * @param {Mixed} date The date value
19670 * Fires when select a date.
19671 * @param {Roo.bootstrap.DateField} this
19672 * @param {Mixed} date The date value
19678 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19681 * @cfg {String} format
19682 * The default time format string which can be overriden for localization support. The format must be
19683 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19687 onRender: function(ct, position)
19690 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19692 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19694 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19696 this.pop = this.picker().select('>.datepicker-time',true).first();
19697 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19699 this.picker().on('mousedown', this.onMousedown, this);
19700 this.picker().on('click', this.onClick, this);
19702 this.picker().addClass('datepicker-dropdown');
19707 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19708 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19709 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19710 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19711 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19712 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19716 fireKey: function(e){
19717 if (!this.picker().isVisible()){
19718 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19724 e.preventDefault();
19732 this.onTogglePeriod();
19735 this.onIncrementMinutes();
19738 this.onDecrementMinutes();
19747 onClick: function(e) {
19748 e.stopPropagation();
19749 e.preventDefault();
19752 picker : function()
19754 return this.el.select('.datepicker', true).first();
19757 fillTime: function()
19759 var time = this.pop.select('tbody', true).first();
19761 time.dom.innerHTML = '';
19776 cls: 'hours-up glyphicon glyphicon-chevron-up'
19796 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19817 cls: 'timepicker-hour',
19832 cls: 'timepicker-minute',
19847 cls: 'btn btn-primary period',
19869 cls: 'hours-down glyphicon glyphicon-chevron-down'
19889 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19907 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19914 var hours = this.time.getHours();
19915 var minutes = this.time.getMinutes();
19928 hours = hours - 12;
19932 hours = '0' + hours;
19936 minutes = '0' + minutes;
19939 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19940 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19941 this.pop.select('button', true).first().dom.innerHTML = period;
19947 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19949 var cls = ['bottom'];
19951 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19958 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19963 this.picker().addClass(cls.join('-'));
19967 Roo.each(cls, function(c){
19969 _this.picker().setTop(_this.inputEl().getHeight());
19973 _this.picker().setTop(0 - _this.picker().getHeight());
19978 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19982 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19989 onFocus : function()
19991 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19995 onBlur : function()
19997 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20003 this.picker().show();
20008 this.fireEvent('show', this, this.date);
20013 this.picker().hide();
20016 this.fireEvent('hide', this, this.date);
20019 setTime : function()
20022 this.setValue(this.time.format(this.format));
20024 this.fireEvent('select', this, this.date);
20029 onMousedown: function(e){
20030 e.stopPropagation();
20031 e.preventDefault();
20034 onIncrementHours: function()
20036 Roo.log('onIncrementHours');
20037 this.time = this.time.add(Date.HOUR, 1);
20042 onDecrementHours: function()
20044 Roo.log('onDecrementHours');
20045 this.time = this.time.add(Date.HOUR, -1);
20049 onIncrementMinutes: function()
20051 Roo.log('onIncrementMinutes');
20052 this.time = this.time.add(Date.MINUTE, 1);
20056 onDecrementMinutes: function()
20058 Roo.log('onDecrementMinutes');
20059 this.time = this.time.add(Date.MINUTE, -1);
20063 onTogglePeriod: function()
20065 Roo.log('onTogglePeriod');
20066 this.time = this.time.add(Date.HOUR, 12);
20073 Roo.apply(Roo.bootstrap.TimeField, {
20103 cls: 'btn btn-info ok',
20115 Roo.apply(Roo.bootstrap.TimeField, {
20119 cls: 'datepicker dropdown-menu',
20123 cls: 'datepicker-time',
20127 cls: 'table-condensed',
20129 Roo.bootstrap.TimeField.content,
20130 Roo.bootstrap.TimeField.footer
20149 * @class Roo.bootstrap.MonthField
20150 * @extends Roo.bootstrap.Input
20151 * Bootstrap MonthField class
20153 * @cfg {String} language default en
20156 * Create a new MonthField
20157 * @param {Object} config The config object
20160 Roo.bootstrap.MonthField = function(config){
20161 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20166 * Fires when this field show.
20167 * @param {Roo.bootstrap.MonthField} this
20168 * @param {Mixed} date The date value
20173 * Fires when this field hide.
20174 * @param {Roo.bootstrap.MonthField} this
20175 * @param {Mixed} date The date value
20180 * Fires when select a date.
20181 * @param {Roo.bootstrap.MonthField} this
20182 * @param {String} oldvalue The old value
20183 * @param {String} newvalue The new value
20189 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20191 onRender: function(ct, position)
20194 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20196 this.language = this.language || 'en';
20197 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20198 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20200 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20201 this.isInline = false;
20202 this.isInput = true;
20203 this.component = this.el.select('.add-on', true).first() || false;
20204 this.component = (this.component && this.component.length === 0) ? false : this.component;
20205 this.hasInput = this.component && this.inputEL().length;
20207 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20209 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20211 this.picker().on('mousedown', this.onMousedown, this);
20212 this.picker().on('click', this.onClick, this);
20214 this.picker().addClass('datepicker-dropdown');
20216 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20217 v.setStyle('width', '189px');
20224 if(this.isInline) {
20230 setValue: function(v, suppressEvent)
20232 var o = this.getValue();
20234 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20238 if(suppressEvent !== true){
20239 this.fireEvent('select', this, o, v);
20244 getValue: function()
20249 onClick: function(e)
20251 e.stopPropagation();
20252 e.preventDefault();
20254 var target = e.getTarget();
20256 if(target.nodeName.toLowerCase() === 'i'){
20257 target = Roo.get(target).dom.parentNode;
20260 var nodeName = target.nodeName;
20261 var className = target.className;
20262 var html = target.innerHTML;
20264 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20268 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20270 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20276 picker : function()
20278 return this.pickerEl;
20281 fillMonths: function()
20284 var months = this.picker().select('>.datepicker-months td', true).first();
20286 months.dom.innerHTML = '';
20292 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20295 months.createChild(month);
20304 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20305 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20308 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20309 e.removeClass('active');
20311 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20312 e.addClass('active');
20319 if(this.isInline) {
20323 this.picker().removeClass(['bottom', 'top']);
20325 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20327 * place to the top of element!
20331 this.picker().addClass('top');
20332 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20337 this.picker().addClass('bottom');
20339 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20342 onFocus : function()
20344 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20348 onBlur : function()
20350 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20352 var d = this.inputEl().getValue();
20361 this.picker().show();
20362 this.picker().select('>.datepicker-months', true).first().show();
20366 this.fireEvent('show', this, this.date);
20371 if(this.isInline) {
20374 this.picker().hide();
20375 this.fireEvent('hide', this, this.date);
20379 onMousedown: function(e)
20381 e.stopPropagation();
20382 e.preventDefault();
20387 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20391 fireKey: function(e)
20393 if (!this.picker().isVisible()){
20394 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20405 e.preventDefault();
20409 dir = e.keyCode == 37 ? -1 : 1;
20411 this.vIndex = this.vIndex + dir;
20413 if(this.vIndex < 0){
20417 if(this.vIndex > 11){
20421 if(isNaN(this.vIndex)){
20425 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20431 dir = e.keyCode == 38 ? -1 : 1;
20433 this.vIndex = this.vIndex + dir * 4;
20435 if(this.vIndex < 0){
20439 if(this.vIndex > 11){
20443 if(isNaN(this.vIndex)){
20447 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20452 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20453 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20457 e.preventDefault();
20460 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20461 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20477 this.picker().remove();
20482 Roo.apply(Roo.bootstrap.MonthField, {
20501 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20502 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20507 Roo.apply(Roo.bootstrap.MonthField, {
20511 cls: 'datepicker dropdown-menu roo-dynamic',
20515 cls: 'datepicker-months',
20519 cls: 'table-condensed',
20521 Roo.bootstrap.DateField.content
20541 * @class Roo.bootstrap.CheckBox
20542 * @extends Roo.bootstrap.Input
20543 * Bootstrap CheckBox class
20545 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20546 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20547 * @cfg {String} boxLabel The text that appears beside the checkbox
20548 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20549 * @cfg {Boolean} checked initnal the element
20550 * @cfg {Boolean} inline inline the element (default false)
20551 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20552 * @cfg {String} tooltip label tooltip
20555 * Create a new CheckBox
20556 * @param {Object} config The config object
20559 Roo.bootstrap.CheckBox = function(config){
20560 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20565 * Fires when the element is checked or unchecked.
20566 * @param {Roo.bootstrap.CheckBox} this This input
20567 * @param {Boolean} checked The new checked value
20572 * Fires when the element is click.
20573 * @param {Roo.bootstrap.CheckBox} this This input
20580 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20582 inputType: 'checkbox',
20591 getAutoCreate : function()
20593 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20599 cfg.cls = 'form-group ' + this.inputType; //input-group
20602 cfg.cls += ' ' + this.inputType + '-inline';
20608 type : this.inputType,
20609 value : this.inputValue,
20610 cls : 'roo-' + this.inputType, //'form-box',
20611 placeholder : this.placeholder || ''
20615 if(this.inputType != 'radio'){
20619 cls : 'roo-hidden-value',
20620 value : this.checked ? this.inputValue : this.valueOff
20625 if (this.weight) { // Validity check?
20626 cfg.cls += " " + this.inputType + "-" + this.weight;
20629 if (this.disabled) {
20630 input.disabled=true;
20634 input.checked = this.checked;
20639 input.name = this.name;
20641 if(this.inputType != 'radio'){
20642 hidden.name = this.name;
20643 input.name = '_hidden_' + this.name;
20648 input.cls += ' input-' + this.size;
20653 ['xs','sm','md','lg'].map(function(size){
20654 if (settings[size]) {
20655 cfg.cls += ' col-' + size + '-' + settings[size];
20659 var inputblock = input;
20661 if (this.before || this.after) {
20664 cls : 'input-group',
20669 inputblock.cn.push({
20671 cls : 'input-group-addon',
20676 inputblock.cn.push(input);
20678 if(this.inputType != 'radio'){
20679 inputblock.cn.push(hidden);
20683 inputblock.cn.push({
20685 cls : 'input-group-addon',
20692 if (align ==='left' && this.fieldLabel.length) {
20693 // Roo.log("left and has label");
20698 cls : 'control-label',
20699 html : this.fieldLabel
20709 if(this.labelWidth > 12){
20710 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20713 if(this.labelWidth < 13 && this.labelmd == 0){
20714 this.labelmd = this.labelWidth;
20717 if(this.labellg > 0){
20718 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20719 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20722 if(this.labelmd > 0){
20723 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20724 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20727 if(this.labelsm > 0){
20728 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20729 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20732 if(this.labelxs > 0){
20733 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20734 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20737 } else if ( this.fieldLabel.length) {
20738 // Roo.log(" label");
20742 tag: this.boxLabel ? 'span' : 'label',
20744 cls: 'control-label box-input-label',
20745 //cls : 'input-group-addon',
20746 html : this.fieldLabel
20755 // Roo.log(" no label && no align");
20756 cfg.cn = [ inputblock ] ;
20762 var boxLabelCfg = {
20764 //'for': id, // box label is handled by onclick - so no for...
20766 html: this.boxLabel
20770 boxLabelCfg.tooltip = this.tooltip;
20773 cfg.cn.push(boxLabelCfg);
20776 if(this.inputType != 'radio'){
20777 cfg.cn.push(hidden);
20785 * return the real input element.
20787 inputEl: function ()
20789 return this.el.select('input.roo-' + this.inputType,true).first();
20791 hiddenEl: function ()
20793 return this.el.select('input.roo-hidden-value',true).first();
20796 labelEl: function()
20798 return this.el.select('label.control-label',true).first();
20800 /* depricated... */
20804 return this.labelEl();
20807 boxLabelEl: function()
20809 return this.el.select('label.box-label',true).first();
20812 initEvents : function()
20814 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20816 this.inputEl().on('click', this.onClick, this);
20818 if (this.boxLabel) {
20819 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20822 this.startValue = this.getValue();
20825 Roo.bootstrap.CheckBox.register(this);
20829 onClick : function(e)
20831 if(this.fireEvent('click', this, e) !== false){
20832 this.setChecked(!this.checked);
20837 setChecked : function(state,suppressEvent)
20839 this.startValue = this.getValue();
20841 if(this.inputType == 'radio'){
20843 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20844 e.dom.checked = false;
20847 this.inputEl().dom.checked = true;
20849 this.inputEl().dom.value = this.inputValue;
20851 if(suppressEvent !== true){
20852 this.fireEvent('check', this, true);
20860 this.checked = state;
20862 this.inputEl().dom.checked = state;
20865 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20867 if(suppressEvent !== true){
20868 this.fireEvent('check', this, state);
20874 getValue : function()
20876 if(this.inputType == 'radio'){
20877 return this.getGroupValue();
20880 return this.hiddenEl().dom.value;
20884 getGroupValue : function()
20886 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20890 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20893 setValue : function(v,suppressEvent)
20895 if(this.inputType == 'radio'){
20896 this.setGroupValue(v, suppressEvent);
20900 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20905 setGroupValue : function(v, suppressEvent)
20907 this.startValue = this.getValue();
20909 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20910 e.dom.checked = false;
20912 if(e.dom.value == v){
20913 e.dom.checked = true;
20917 if(suppressEvent !== true){
20918 this.fireEvent('check', this, true);
20926 validate : function()
20928 if(this.getVisibilityEl().hasClass('hidden')){
20934 (this.inputType == 'radio' && this.validateRadio()) ||
20935 (this.inputType == 'checkbox' && this.validateCheckbox())
20941 this.markInvalid();
20945 validateRadio : function()
20947 if(this.getVisibilityEl().hasClass('hidden')){
20951 if(this.allowBlank){
20957 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20958 if(!e.dom.checked){
20970 validateCheckbox : function()
20973 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20974 //return (this.getValue() == this.inputValue) ? true : false;
20977 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20985 for(var i in group){
20986 if(group[i].el.isVisible(true)){
20994 for(var i in group){
20999 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21006 * Mark this field as valid
21008 markValid : function()
21012 this.fireEvent('valid', this);
21014 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21017 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21024 if(this.inputType == 'radio'){
21025 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21026 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21027 e.findParent('.form-group', false, true).addClass(_this.validClass);
21034 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21035 this.el.findParent('.form-group', false, true).addClass(this.validClass);
21039 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21045 for(var i in group){
21046 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21047 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21052 * Mark this field as invalid
21053 * @param {String} msg The validation message
21055 markInvalid : function(msg)
21057 if(this.allowBlank){
21063 this.fireEvent('invalid', this, msg);
21065 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21068 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21072 label.markInvalid();
21075 if(this.inputType == 'radio'){
21076 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21077 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21078 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21085 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21086 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21090 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21096 for(var i in group){
21097 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21098 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21103 clearInvalid : function()
21105 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21107 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21109 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21111 if (label && label.iconEl) {
21112 label.iconEl.removeClass(label.validClass);
21113 label.iconEl.removeClass(label.invalidClass);
21117 disable : function()
21119 if(this.inputType != 'radio'){
21120 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21127 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21128 _this.getActionEl().addClass(this.disabledClass);
21129 e.dom.disabled = true;
21133 this.disabled = true;
21134 this.fireEvent("disable", this);
21138 enable : function()
21140 if(this.inputType != 'radio'){
21141 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21148 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21149 _this.getActionEl().removeClass(this.disabledClass);
21150 e.dom.disabled = false;
21154 this.disabled = false;
21155 this.fireEvent("enable", this);
21159 setBoxLabel : function(v)
21164 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21170 Roo.apply(Roo.bootstrap.CheckBox, {
21175 * register a CheckBox Group
21176 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21178 register : function(checkbox)
21180 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21181 this.groups[checkbox.groupId] = {};
21184 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21188 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21192 * fetch a CheckBox Group based on the group ID
21193 * @param {string} the group ID
21194 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21196 get: function(groupId) {
21197 if (typeof(this.groups[groupId]) == 'undefined') {
21201 return this.groups[groupId] ;
21214 * @class Roo.bootstrap.Radio
21215 * @extends Roo.bootstrap.Component
21216 * Bootstrap Radio class
21217 * @cfg {String} boxLabel - the label associated
21218 * @cfg {String} value - the value of radio
21221 * Create a new Radio
21222 * @param {Object} config The config object
21224 Roo.bootstrap.Radio = function(config){
21225 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21229 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21235 getAutoCreate : function()
21239 cls : 'form-group radio',
21244 html : this.boxLabel
21252 initEvents : function()
21254 this.parent().register(this);
21256 this.el.on('click', this.onClick, this);
21260 onClick : function(e)
21262 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21263 this.setChecked(true);
21267 setChecked : function(state, suppressEvent)
21269 this.parent().setValue(this.value, suppressEvent);
21273 setBoxLabel : function(v)
21278 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21293 * @class Roo.bootstrap.SecurePass
21294 * @extends Roo.bootstrap.Input
21295 * Bootstrap SecurePass class
21299 * Create a new SecurePass
21300 * @param {Object} config The config object
21303 Roo.bootstrap.SecurePass = function (config) {
21304 // these go here, so the translation tool can replace them..
21306 PwdEmpty: "Please type a password, and then retype it to confirm.",
21307 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21308 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21309 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21310 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21311 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21312 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21313 TooWeak: "Your password is Too Weak."
21315 this.meterLabel = "Password strength:";
21316 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21317 this.meterClass = [
21318 "roo-password-meter-tooweak",
21319 "roo-password-meter-weak",
21320 "roo-password-meter-medium",
21321 "roo-password-meter-strong",
21322 "roo-password-meter-grey"
21327 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21330 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21332 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21334 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21335 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21336 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21337 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21338 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21339 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21340 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21350 * @cfg {String/Object} Label for the strength meter (defaults to
21351 * 'Password strength:')
21356 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21357 * ['Weak', 'Medium', 'Strong'])
21360 pwdStrengths: false,
21373 initEvents: function ()
21375 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21377 if (this.el.is('input[type=password]') && Roo.isSafari) {
21378 this.el.on('keydown', this.SafariOnKeyDown, this);
21381 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21384 onRender: function (ct, position)
21386 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21387 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21388 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21390 this.trigger.createChild({
21395 cls: 'roo-password-meter-grey col-xs-12',
21398 //width: this.meterWidth + 'px'
21402 cls: 'roo-password-meter-text'
21408 if (this.hideTrigger) {
21409 this.trigger.setDisplayed(false);
21411 this.setSize(this.width || '', this.height || '');
21414 onDestroy: function ()
21416 if (this.trigger) {
21417 this.trigger.removeAllListeners();
21418 this.trigger.remove();
21421 this.wrap.remove();
21423 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21426 checkStrength: function ()
21428 var pwd = this.inputEl().getValue();
21429 if (pwd == this._lastPwd) {
21434 if (this.ClientSideStrongPassword(pwd)) {
21436 } else if (this.ClientSideMediumPassword(pwd)) {
21438 } else if (this.ClientSideWeakPassword(pwd)) {
21444 Roo.log('strength1: ' + strength);
21446 //var pm = this.trigger.child('div/div/div').dom;
21447 var pm = this.trigger.child('div/div');
21448 pm.removeClass(this.meterClass);
21449 pm.addClass(this.meterClass[strength]);
21452 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21454 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21456 this._lastPwd = pwd;
21460 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21462 this._lastPwd = '';
21464 var pm = this.trigger.child('div/div');
21465 pm.removeClass(this.meterClass);
21466 pm.addClass('roo-password-meter-grey');
21469 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21472 this.inputEl().dom.type='password';
21475 validateValue: function (value)
21478 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21481 if (value.length == 0) {
21482 if (this.allowBlank) {
21483 this.clearInvalid();
21487 this.markInvalid(this.errors.PwdEmpty);
21488 this.errorMsg = this.errors.PwdEmpty;
21496 if ('[\x21-\x7e]*'.match(value)) {
21497 this.markInvalid(this.errors.PwdBadChar);
21498 this.errorMsg = this.errors.PwdBadChar;
21501 if (value.length < 6) {
21502 this.markInvalid(this.errors.PwdShort);
21503 this.errorMsg = this.errors.PwdShort;
21506 if (value.length > 16) {
21507 this.markInvalid(this.errors.PwdLong);
21508 this.errorMsg = this.errors.PwdLong;
21512 if (this.ClientSideStrongPassword(value)) {
21514 } else if (this.ClientSideMediumPassword(value)) {
21516 } else if (this.ClientSideWeakPassword(value)) {
21523 if (strength < 2) {
21524 //this.markInvalid(this.errors.TooWeak);
21525 this.errorMsg = this.errors.TooWeak;
21530 console.log('strength2: ' + strength);
21532 //var pm = this.trigger.child('div/div/div').dom;
21534 var pm = this.trigger.child('div/div');
21535 pm.removeClass(this.meterClass);
21536 pm.addClass(this.meterClass[strength]);
21538 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21540 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21542 this.errorMsg = '';
21546 CharacterSetChecks: function (type)
21549 this.fResult = false;
21552 isctype: function (character, type)
21555 case this.kCapitalLetter:
21556 if (character >= 'A' && character <= 'Z') {
21561 case this.kSmallLetter:
21562 if (character >= 'a' && character <= 'z') {
21568 if (character >= '0' && character <= '9') {
21573 case this.kPunctuation:
21574 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21585 IsLongEnough: function (pwd, size)
21587 return !(pwd == null || isNaN(size) || pwd.length < size);
21590 SpansEnoughCharacterSets: function (word, nb)
21592 if (!this.IsLongEnough(word, nb))
21597 var characterSetChecks = new Array(
21598 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21599 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21602 for (var index = 0; index < word.length; ++index) {
21603 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21604 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21605 characterSetChecks[nCharSet].fResult = true;
21612 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21613 if (characterSetChecks[nCharSet].fResult) {
21618 if (nCharSets < nb) {
21624 ClientSideStrongPassword: function (pwd)
21626 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21629 ClientSideMediumPassword: function (pwd)
21631 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21634 ClientSideWeakPassword: function (pwd)
21636 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21639 })//<script type="text/javascript">
21642 * Based Ext JS Library 1.1.1
21643 * Copyright(c) 2006-2007, Ext JS, LLC.
21649 * @class Roo.HtmlEditorCore
21650 * @extends Roo.Component
21651 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21653 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21656 Roo.HtmlEditorCore = function(config){
21659 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21664 * @event initialize
21665 * Fires when the editor is fully initialized (including the iframe)
21666 * @param {Roo.HtmlEditorCore} this
21671 * Fires when the editor is first receives the focus. Any insertion must wait
21672 * until after this event.
21673 * @param {Roo.HtmlEditorCore} this
21677 * @event beforesync
21678 * Fires before the textarea is updated with content from the editor iframe. Return false
21679 * to cancel the sync.
21680 * @param {Roo.HtmlEditorCore} this
21681 * @param {String} html
21685 * @event beforepush
21686 * Fires before the iframe editor is updated with content from the textarea. Return false
21687 * to cancel the push.
21688 * @param {Roo.HtmlEditorCore} this
21689 * @param {String} html
21694 * Fires when the textarea is updated with content from the editor iframe.
21695 * @param {Roo.HtmlEditorCore} this
21696 * @param {String} html
21701 * Fires when the iframe editor is updated with content from the textarea.
21702 * @param {Roo.HtmlEditorCore} this
21703 * @param {String} html
21708 * @event editorevent
21709 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21710 * @param {Roo.HtmlEditorCore} this
21716 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21718 // defaults : white / black...
21719 this.applyBlacklists();
21726 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21730 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21736 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21741 * @cfg {Number} height (in pixels)
21745 * @cfg {Number} width (in pixels)
21750 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21753 stylesheets: false,
21758 // private properties
21759 validationEvent : false,
21761 initialized : false,
21763 sourceEditMode : false,
21764 onFocus : Roo.emptyFn,
21766 hideMode:'offsets',
21770 // blacklist + whitelisted elements..
21777 * Protected method that will not generally be called directly. It
21778 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21779 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21781 getDocMarkup : function(){
21785 // inherit styels from page...??
21786 if (this.stylesheets === false) {
21788 Roo.get(document.head).select('style').each(function(node) {
21789 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21792 Roo.get(document.head).select('link').each(function(node) {
21793 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21796 } else if (!this.stylesheets.length) {
21798 st = '<style type="text/css">' +
21799 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21802 st = '<style type="text/css">' +
21807 st += '<style type="text/css">' +
21808 'IMG { cursor: pointer } ' +
21811 var cls = 'roo-htmleditor-body';
21813 if(this.bodyCls.length){
21814 cls += ' ' + this.bodyCls;
21817 return '<html><head>' + st +
21818 //<style type="text/css">' +
21819 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21821 ' </head><body class="' + cls + '"></body></html>';
21825 onRender : function(ct, position)
21828 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21829 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21832 this.el.dom.style.border = '0 none';
21833 this.el.dom.setAttribute('tabIndex', -1);
21834 this.el.addClass('x-hidden hide');
21838 if(Roo.isIE){ // fix IE 1px bogus margin
21839 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21843 this.frameId = Roo.id();
21847 var iframe = this.owner.wrap.createChild({
21849 cls: 'form-control', // bootstrap..
21851 name: this.frameId,
21852 frameBorder : 'no',
21853 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21858 this.iframe = iframe.dom;
21860 this.assignDocWin();
21862 this.doc.designMode = 'on';
21865 this.doc.write(this.getDocMarkup());
21869 var task = { // must defer to wait for browser to be ready
21871 //console.log("run task?" + this.doc.readyState);
21872 this.assignDocWin();
21873 if(this.doc.body || this.doc.readyState == 'complete'){
21875 this.doc.designMode="on";
21879 Roo.TaskMgr.stop(task);
21880 this.initEditor.defer(10, this);
21887 Roo.TaskMgr.start(task);
21892 onResize : function(w, h)
21894 Roo.log('resize: ' +w + ',' + h );
21895 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21899 if(typeof w == 'number'){
21901 this.iframe.style.width = w + 'px';
21903 if(typeof h == 'number'){
21905 this.iframe.style.height = h + 'px';
21907 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21914 * Toggles the editor between standard and source edit mode.
21915 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21917 toggleSourceEdit : function(sourceEditMode){
21919 this.sourceEditMode = sourceEditMode === true;
21921 if(this.sourceEditMode){
21923 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21926 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21927 //this.iframe.className = '';
21930 //this.setSize(this.owner.wrap.getSize());
21931 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21938 * Protected method that will not generally be called directly. If you need/want
21939 * custom HTML cleanup, this is the method you should override.
21940 * @param {String} html The HTML to be cleaned
21941 * return {String} The cleaned HTML
21943 cleanHtml : function(html){
21944 html = String(html);
21945 if(html.length > 5){
21946 if(Roo.isSafari){ // strip safari nonsense
21947 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21950 if(html == ' '){
21957 * HTML Editor -> Textarea
21958 * Protected method that will not generally be called directly. Syncs the contents
21959 * of the editor iframe with the textarea.
21961 syncValue : function(){
21962 if(this.initialized){
21963 var bd = (this.doc.body || this.doc.documentElement);
21964 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21965 var html = bd.innerHTML;
21967 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21968 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21970 html = '<div style="'+m[0]+'">' + html + '</div>';
21973 html = this.cleanHtml(html);
21974 // fix up the special chars.. normaly like back quotes in word...
21975 // however we do not want to do this with chinese..
21976 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21977 var cc = b.charCodeAt();
21979 (cc >= 0x4E00 && cc < 0xA000 ) ||
21980 (cc >= 0x3400 && cc < 0x4E00 ) ||
21981 (cc >= 0xf900 && cc < 0xfb00 )
21987 if(this.owner.fireEvent('beforesync', this, html) !== false){
21988 this.el.dom.value = html;
21989 this.owner.fireEvent('sync', this, html);
21995 * Protected method that will not generally be called directly. Pushes the value of the textarea
21996 * into the iframe editor.
21998 pushValue : function(){
21999 if(this.initialized){
22000 var v = this.el.dom.value.trim();
22002 // if(v.length < 1){
22006 if(this.owner.fireEvent('beforepush', this, v) !== false){
22007 var d = (this.doc.body || this.doc.documentElement);
22009 this.cleanUpPaste();
22010 this.el.dom.value = d.innerHTML;
22011 this.owner.fireEvent('push', this, v);
22017 deferFocus : function(){
22018 this.focus.defer(10, this);
22022 focus : function(){
22023 if(this.win && !this.sourceEditMode){
22030 assignDocWin: function()
22032 var iframe = this.iframe;
22035 this.doc = iframe.contentWindow.document;
22036 this.win = iframe.contentWindow;
22038 // if (!Roo.get(this.frameId)) {
22041 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22042 // this.win = Roo.get(this.frameId).dom.contentWindow;
22044 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22048 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22049 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22054 initEditor : function(){
22055 //console.log("INIT EDITOR");
22056 this.assignDocWin();
22060 this.doc.designMode="on";
22062 this.doc.write(this.getDocMarkup());
22065 var dbody = (this.doc.body || this.doc.documentElement);
22066 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22067 // this copies styles from the containing element into thsi one..
22068 // not sure why we need all of this..
22069 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22071 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22072 //ss['background-attachment'] = 'fixed'; // w3c
22073 dbody.bgProperties = 'fixed'; // ie
22074 //Roo.DomHelper.applyStyles(dbody, ss);
22075 Roo.EventManager.on(this.doc, {
22076 //'mousedown': this.onEditorEvent,
22077 'mouseup': this.onEditorEvent,
22078 'dblclick': this.onEditorEvent,
22079 'click': this.onEditorEvent,
22080 'keyup': this.onEditorEvent,
22085 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22087 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22088 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22090 this.initialized = true;
22092 this.owner.fireEvent('initialize', this);
22097 onDestroy : function(){
22103 //for (var i =0; i < this.toolbars.length;i++) {
22104 // // fixme - ask toolbars for heights?
22105 // this.toolbars[i].onDestroy();
22108 //this.wrap.dom.innerHTML = '';
22109 //this.wrap.remove();
22114 onFirstFocus : function(){
22116 this.assignDocWin();
22119 this.activated = true;
22122 if(Roo.isGecko){ // prevent silly gecko errors
22124 var s = this.win.getSelection();
22125 if(!s.focusNode || s.focusNode.nodeType != 3){
22126 var r = s.getRangeAt(0);
22127 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22132 this.execCmd('useCSS', true);
22133 this.execCmd('styleWithCSS', false);
22136 this.owner.fireEvent('activate', this);
22140 adjustFont: function(btn){
22141 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22142 //if(Roo.isSafari){ // safari
22145 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22146 if(Roo.isSafari){ // safari
22147 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22148 v = (v < 10) ? 10 : v;
22149 v = (v > 48) ? 48 : v;
22150 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22155 v = Math.max(1, v+adjust);
22157 this.execCmd('FontSize', v );
22160 onEditorEvent : function(e)
22162 this.owner.fireEvent('editorevent', this, e);
22163 // this.updateToolbar();
22164 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22167 insertTag : function(tg)
22169 // could be a bit smarter... -> wrap the current selected tRoo..
22170 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22172 range = this.createRange(this.getSelection());
22173 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22174 wrappingNode.appendChild(range.extractContents());
22175 range.insertNode(wrappingNode);
22182 this.execCmd("formatblock", tg);
22186 insertText : function(txt)
22190 var range = this.createRange();
22191 range.deleteContents();
22192 //alert(Sender.getAttribute('label'));
22194 range.insertNode(this.doc.createTextNode(txt));
22200 * Executes a Midas editor command on the editor document and performs necessary focus and
22201 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22202 * @param {String} cmd The Midas command
22203 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22205 relayCmd : function(cmd, value){
22207 this.execCmd(cmd, value);
22208 this.owner.fireEvent('editorevent', this);
22209 //this.updateToolbar();
22210 this.owner.deferFocus();
22214 * Executes a Midas editor command directly on the editor document.
22215 * For visual commands, you should use {@link #relayCmd} instead.
22216 * <b>This should only be called after the editor is initialized.</b>
22217 * @param {String} cmd The Midas command
22218 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22220 execCmd : function(cmd, value){
22221 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22228 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22230 * @param {String} text | dom node..
22232 insertAtCursor : function(text)
22235 if(!this.activated){
22241 var r = this.doc.selection.createRange();
22252 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22256 // from jquery ui (MIT licenced)
22258 var win = this.win;
22260 if (win.getSelection && win.getSelection().getRangeAt) {
22261 range = win.getSelection().getRangeAt(0);
22262 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22263 range.insertNode(node);
22264 } else if (win.document.selection && win.document.selection.createRange) {
22265 // no firefox support
22266 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22267 win.document.selection.createRange().pasteHTML(txt);
22269 // no firefox support
22270 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22271 this.execCmd('InsertHTML', txt);
22280 mozKeyPress : function(e){
22282 var c = e.getCharCode(), cmd;
22285 c = String.fromCharCode(c).toLowerCase();
22299 this.cleanUpPaste.defer(100, this);
22307 e.preventDefault();
22315 fixKeys : function(){ // load time branching for fastest keydown performance
22317 return function(e){
22318 var k = e.getKey(), r;
22321 r = this.doc.selection.createRange();
22324 r.pasteHTML('    ');
22331 r = this.doc.selection.createRange();
22333 var target = r.parentElement();
22334 if(!target || target.tagName.toLowerCase() != 'li'){
22336 r.pasteHTML('<br />');
22342 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22343 this.cleanUpPaste.defer(100, this);
22349 }else if(Roo.isOpera){
22350 return function(e){
22351 var k = e.getKey();
22355 this.execCmd('InsertHTML','    ');
22358 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22359 this.cleanUpPaste.defer(100, this);
22364 }else if(Roo.isSafari){
22365 return function(e){
22366 var k = e.getKey();
22370 this.execCmd('InsertText','\t');
22374 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22375 this.cleanUpPaste.defer(100, this);
22383 getAllAncestors: function()
22385 var p = this.getSelectedNode();
22388 a.push(p); // push blank onto stack..
22389 p = this.getParentElement();
22393 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22397 a.push(this.doc.body);
22401 lastSelNode : false,
22404 getSelection : function()
22406 this.assignDocWin();
22407 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22410 getSelectedNode: function()
22412 // this may only work on Gecko!!!
22414 // should we cache this!!!!
22419 var range = this.createRange(this.getSelection()).cloneRange();
22422 var parent = range.parentElement();
22424 var testRange = range.duplicate();
22425 testRange.moveToElementText(parent);
22426 if (testRange.inRange(range)) {
22429 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22432 parent = parent.parentElement;
22437 // is ancestor a text element.
22438 var ac = range.commonAncestorContainer;
22439 if (ac.nodeType == 3) {
22440 ac = ac.parentNode;
22443 var ar = ac.childNodes;
22446 var other_nodes = [];
22447 var has_other_nodes = false;
22448 for (var i=0;i<ar.length;i++) {
22449 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22452 // fullly contained node.
22454 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22459 // probably selected..
22460 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22461 other_nodes.push(ar[i]);
22465 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22470 has_other_nodes = true;
22472 if (!nodes.length && other_nodes.length) {
22473 nodes= other_nodes;
22475 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22481 createRange: function(sel)
22483 // this has strange effects when using with
22484 // top toolbar - not sure if it's a great idea.
22485 //this.editor.contentWindow.focus();
22486 if (typeof sel != "undefined") {
22488 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22490 return this.doc.createRange();
22493 return this.doc.createRange();
22496 getParentElement: function()
22499 this.assignDocWin();
22500 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22502 var range = this.createRange(sel);
22505 var p = range.commonAncestorContainer;
22506 while (p.nodeType == 3) { // text node
22517 * Range intersection.. the hard stuff...
22521 * [ -- selected range --- ]
22525 * if end is before start or hits it. fail.
22526 * if start is after end or hits it fail.
22528 * if either hits (but other is outside. - then it's not
22534 // @see http://www.thismuchiknow.co.uk/?p=64.
22535 rangeIntersectsNode : function(range, node)
22537 var nodeRange = node.ownerDocument.createRange();
22539 nodeRange.selectNode(node);
22541 nodeRange.selectNodeContents(node);
22544 var rangeStartRange = range.cloneRange();
22545 rangeStartRange.collapse(true);
22547 var rangeEndRange = range.cloneRange();
22548 rangeEndRange.collapse(false);
22550 var nodeStartRange = nodeRange.cloneRange();
22551 nodeStartRange.collapse(true);
22553 var nodeEndRange = nodeRange.cloneRange();
22554 nodeEndRange.collapse(false);
22556 return rangeStartRange.compareBoundaryPoints(
22557 Range.START_TO_START, nodeEndRange) == -1 &&
22558 rangeEndRange.compareBoundaryPoints(
22559 Range.START_TO_START, nodeStartRange) == 1;
22563 rangeCompareNode : function(range, node)
22565 var nodeRange = node.ownerDocument.createRange();
22567 nodeRange.selectNode(node);
22569 nodeRange.selectNodeContents(node);
22573 range.collapse(true);
22575 nodeRange.collapse(true);
22577 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22578 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22580 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22582 var nodeIsBefore = ss == 1;
22583 var nodeIsAfter = ee == -1;
22585 if (nodeIsBefore && nodeIsAfter) {
22588 if (!nodeIsBefore && nodeIsAfter) {
22589 return 1; //right trailed.
22592 if (nodeIsBefore && !nodeIsAfter) {
22593 return 2; // left trailed.
22599 // private? - in a new class?
22600 cleanUpPaste : function()
22602 // cleans up the whole document..
22603 Roo.log('cleanuppaste');
22605 this.cleanUpChildren(this.doc.body);
22606 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22607 if (clean != this.doc.body.innerHTML) {
22608 this.doc.body.innerHTML = clean;
22613 cleanWordChars : function(input) {// change the chars to hex code
22614 var he = Roo.HtmlEditorCore;
22616 var output = input;
22617 Roo.each(he.swapCodes, function(sw) {
22618 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22620 output = output.replace(swapper, sw[1]);
22627 cleanUpChildren : function (n)
22629 if (!n.childNodes.length) {
22632 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22633 this.cleanUpChild(n.childNodes[i]);
22640 cleanUpChild : function (node)
22643 //console.log(node);
22644 if (node.nodeName == "#text") {
22645 // clean up silly Windows -- stuff?
22648 if (node.nodeName == "#comment") {
22649 node.parentNode.removeChild(node);
22650 // clean up silly Windows -- stuff?
22653 var lcname = node.tagName.toLowerCase();
22654 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22655 // whitelist of tags..
22657 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22659 node.parentNode.removeChild(node);
22664 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22666 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22667 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22669 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22670 // remove_keep_children = true;
22673 if (remove_keep_children) {
22674 this.cleanUpChildren(node);
22675 // inserts everything just before this node...
22676 while (node.childNodes.length) {
22677 var cn = node.childNodes[0];
22678 node.removeChild(cn);
22679 node.parentNode.insertBefore(cn, node);
22681 node.parentNode.removeChild(node);
22685 if (!node.attributes || !node.attributes.length) {
22686 this.cleanUpChildren(node);
22690 function cleanAttr(n,v)
22693 if (v.match(/^\./) || v.match(/^\//)) {
22696 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22699 if (v.match(/^#/)) {
22702 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22703 node.removeAttribute(n);
22707 var cwhite = this.cwhite;
22708 var cblack = this.cblack;
22710 function cleanStyle(n,v)
22712 if (v.match(/expression/)) { //XSS?? should we even bother..
22713 node.removeAttribute(n);
22717 var parts = v.split(/;/);
22720 Roo.each(parts, function(p) {
22721 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22725 var l = p.split(':').shift().replace(/\s+/g,'');
22726 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22728 if ( cwhite.length && cblack.indexOf(l) > -1) {
22729 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22730 //node.removeAttribute(n);
22734 // only allow 'c whitelisted system attributes'
22735 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22736 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22737 //node.removeAttribute(n);
22747 if (clean.length) {
22748 node.setAttribute(n, clean.join(';'));
22750 node.removeAttribute(n);
22756 for (var i = node.attributes.length-1; i > -1 ; i--) {
22757 var a = node.attributes[i];
22760 if (a.name.toLowerCase().substr(0,2)=='on') {
22761 node.removeAttribute(a.name);
22764 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22765 node.removeAttribute(a.name);
22768 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22769 cleanAttr(a.name,a.value); // fixme..
22772 if (a.name == 'style') {
22773 cleanStyle(a.name,a.value);
22776 /// clean up MS crap..
22777 // tecnically this should be a list of valid class'es..
22780 if (a.name == 'class') {
22781 if (a.value.match(/^Mso/)) {
22782 node.className = '';
22785 if (a.value.match(/^body$/)) {
22786 node.className = '';
22797 this.cleanUpChildren(node);
22803 * Clean up MS wordisms...
22805 cleanWord : function(node)
22810 this.cleanWord(this.doc.body);
22813 if (node.nodeName == "#text") {
22814 // clean up silly Windows -- stuff?
22817 if (node.nodeName == "#comment") {
22818 node.parentNode.removeChild(node);
22819 // clean up silly Windows -- stuff?
22823 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22824 node.parentNode.removeChild(node);
22828 // remove - but keep children..
22829 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22830 while (node.childNodes.length) {
22831 var cn = node.childNodes[0];
22832 node.removeChild(cn);
22833 node.parentNode.insertBefore(cn, node);
22835 node.parentNode.removeChild(node);
22836 this.iterateChildren(node, this.cleanWord);
22840 if (node.className.length) {
22842 var cn = node.className.split(/\W+/);
22844 Roo.each(cn, function(cls) {
22845 if (cls.match(/Mso[a-zA-Z]+/)) {
22850 node.className = cna.length ? cna.join(' ') : '';
22852 node.removeAttribute("class");
22856 if (node.hasAttribute("lang")) {
22857 node.removeAttribute("lang");
22860 if (node.hasAttribute("style")) {
22862 var styles = node.getAttribute("style").split(";");
22864 Roo.each(styles, function(s) {
22865 if (!s.match(/:/)) {
22868 var kv = s.split(":");
22869 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22872 // what ever is left... we allow.
22875 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22876 if (!nstyle.length) {
22877 node.removeAttribute('style');
22880 this.iterateChildren(node, this.cleanWord);
22886 * iterateChildren of a Node, calling fn each time, using this as the scole..
22887 * @param {DomNode} node node to iterate children of.
22888 * @param {Function} fn method of this class to call on each item.
22890 iterateChildren : function(node, fn)
22892 if (!node.childNodes.length) {
22895 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22896 fn.call(this, node.childNodes[i])
22902 * cleanTableWidths.
22904 * Quite often pasting from word etc.. results in tables with column and widths.
22905 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22908 cleanTableWidths : function(node)
22913 this.cleanTableWidths(this.doc.body);
22918 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22921 Roo.log(node.tagName);
22922 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22923 this.iterateChildren(node, this.cleanTableWidths);
22926 if (node.hasAttribute('width')) {
22927 node.removeAttribute('width');
22931 if (node.hasAttribute("style")) {
22934 var styles = node.getAttribute("style").split(";");
22936 Roo.each(styles, function(s) {
22937 if (!s.match(/:/)) {
22940 var kv = s.split(":");
22941 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22944 // what ever is left... we allow.
22947 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22948 if (!nstyle.length) {
22949 node.removeAttribute('style');
22953 this.iterateChildren(node, this.cleanTableWidths);
22961 domToHTML : function(currentElement, depth, nopadtext) {
22963 depth = depth || 0;
22964 nopadtext = nopadtext || false;
22966 if (!currentElement) {
22967 return this.domToHTML(this.doc.body);
22970 //Roo.log(currentElement);
22972 var allText = false;
22973 var nodeName = currentElement.nodeName;
22974 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22976 if (nodeName == '#text') {
22978 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22983 if (nodeName != 'BODY') {
22986 // Prints the node tagName, such as <A>, <IMG>, etc
22989 for(i = 0; i < currentElement.attributes.length;i++) {
22991 var aname = currentElement.attributes.item(i).name;
22992 if (!currentElement.attributes.item(i).value.length) {
22995 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22998 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23007 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23010 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23015 // Traverse the tree
23017 var currentElementChild = currentElement.childNodes.item(i);
23018 var allText = true;
23019 var innerHTML = '';
23021 while (currentElementChild) {
23022 // Formatting code (indent the tree so it looks nice on the screen)
23023 var nopad = nopadtext;
23024 if (lastnode == 'SPAN') {
23028 if (currentElementChild.nodeName == '#text') {
23029 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23030 toadd = nopadtext ? toadd : toadd.trim();
23031 if (!nopad && toadd.length > 80) {
23032 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23034 innerHTML += toadd;
23037 currentElementChild = currentElement.childNodes.item(i);
23043 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23045 // Recursively traverse the tree structure of the child node
23046 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23047 lastnode = currentElementChild.nodeName;
23049 currentElementChild=currentElement.childNodes.item(i);
23055 // The remaining code is mostly for formatting the tree
23056 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23061 ret+= "</"+tagName+">";
23067 applyBlacklists : function()
23069 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23070 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23074 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23075 if (b.indexOf(tag) > -1) {
23078 this.white.push(tag);
23082 Roo.each(w, function(tag) {
23083 if (b.indexOf(tag) > -1) {
23086 if (this.white.indexOf(tag) > -1) {
23089 this.white.push(tag);
23094 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23095 if (w.indexOf(tag) > -1) {
23098 this.black.push(tag);
23102 Roo.each(b, function(tag) {
23103 if (w.indexOf(tag) > -1) {
23106 if (this.black.indexOf(tag) > -1) {
23109 this.black.push(tag);
23114 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23115 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23119 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23120 if (b.indexOf(tag) > -1) {
23123 this.cwhite.push(tag);
23127 Roo.each(w, function(tag) {
23128 if (b.indexOf(tag) > -1) {
23131 if (this.cwhite.indexOf(tag) > -1) {
23134 this.cwhite.push(tag);
23139 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23140 if (w.indexOf(tag) > -1) {
23143 this.cblack.push(tag);
23147 Roo.each(b, function(tag) {
23148 if (w.indexOf(tag) > -1) {
23151 if (this.cblack.indexOf(tag) > -1) {
23154 this.cblack.push(tag);
23159 setStylesheets : function(stylesheets)
23161 if(typeof(stylesheets) == 'string'){
23162 Roo.get(this.iframe.contentDocument.head).createChild({
23164 rel : 'stylesheet',
23173 Roo.each(stylesheets, function(s) {
23178 Roo.get(_this.iframe.contentDocument.head).createChild({
23180 rel : 'stylesheet',
23189 removeStylesheets : function()
23193 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23198 setStyle : function(style)
23200 Roo.get(this.iframe.contentDocument.head).createChild({
23209 // hide stuff that is not compatible
23223 * @event specialkey
23227 * @cfg {String} fieldClass @hide
23230 * @cfg {String} focusClass @hide
23233 * @cfg {String} autoCreate @hide
23236 * @cfg {String} inputType @hide
23239 * @cfg {String} invalidClass @hide
23242 * @cfg {String} invalidText @hide
23245 * @cfg {String} msgFx @hide
23248 * @cfg {String} validateOnBlur @hide
23252 Roo.HtmlEditorCore.white = [
23253 'area', 'br', 'img', 'input', 'hr', 'wbr',
23255 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23256 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23257 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23258 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23259 'table', 'ul', 'xmp',
23261 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23264 'dir', 'menu', 'ol', 'ul', 'dl',
23270 Roo.HtmlEditorCore.black = [
23271 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23273 'base', 'basefont', 'bgsound', 'blink', 'body',
23274 'frame', 'frameset', 'head', 'html', 'ilayer',
23275 'iframe', 'layer', 'link', 'meta', 'object',
23276 'script', 'style' ,'title', 'xml' // clean later..
23278 Roo.HtmlEditorCore.clean = [
23279 'script', 'style', 'title', 'xml'
23281 Roo.HtmlEditorCore.remove = [
23286 Roo.HtmlEditorCore.ablack = [
23290 Roo.HtmlEditorCore.aclean = [
23291 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23295 Roo.HtmlEditorCore.pwhite= [
23296 'http', 'https', 'mailto'
23299 // white listed style attributes.
23300 Roo.HtmlEditorCore.cwhite= [
23301 // 'text-align', /// default is to allow most things..
23307 // black listed style attributes.
23308 Roo.HtmlEditorCore.cblack= [
23309 // 'font-size' -- this can be set by the project
23313 Roo.HtmlEditorCore.swapCodes =[
23332 * @class Roo.bootstrap.HtmlEditor
23333 * @extends Roo.bootstrap.TextArea
23334 * Bootstrap HtmlEditor class
23337 * Create a new HtmlEditor
23338 * @param {Object} config The config object
23341 Roo.bootstrap.HtmlEditor = function(config){
23342 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23343 if (!this.toolbars) {
23344 this.toolbars = [];
23347 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23350 * @event initialize
23351 * Fires when the editor is fully initialized (including the iframe)
23352 * @param {HtmlEditor} this
23357 * Fires when the editor is first receives the focus. Any insertion must wait
23358 * until after this event.
23359 * @param {HtmlEditor} this
23363 * @event beforesync
23364 * Fires before the textarea is updated with content from the editor iframe. Return false
23365 * to cancel the sync.
23366 * @param {HtmlEditor} this
23367 * @param {String} html
23371 * @event beforepush
23372 * Fires before the iframe editor is updated with content from the textarea. Return false
23373 * to cancel the push.
23374 * @param {HtmlEditor} this
23375 * @param {String} html
23380 * Fires when the textarea is updated with content from the editor iframe.
23381 * @param {HtmlEditor} this
23382 * @param {String} html
23387 * Fires when the iframe editor is updated with content from the textarea.
23388 * @param {HtmlEditor} this
23389 * @param {String} html
23393 * @event editmodechange
23394 * Fires when the editor switches edit modes
23395 * @param {HtmlEditor} this
23396 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23398 editmodechange: true,
23400 * @event editorevent
23401 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23402 * @param {HtmlEditor} this
23406 * @event firstfocus
23407 * Fires when on first focus - needed by toolbars..
23408 * @param {HtmlEditor} this
23413 * Auto save the htmlEditor value as a file into Events
23414 * @param {HtmlEditor} this
23418 * @event savedpreview
23419 * preview the saved version of htmlEditor
23420 * @param {HtmlEditor} this
23427 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23431 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23436 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23441 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23446 * @cfg {Number} height (in pixels)
23450 * @cfg {Number} width (in pixels)
23455 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23458 stylesheets: false,
23463 // private properties
23464 validationEvent : false,
23466 initialized : false,
23469 onFocus : Roo.emptyFn,
23471 hideMode:'offsets',
23473 tbContainer : false,
23477 toolbarContainer :function() {
23478 return this.wrap.select('.x-html-editor-tb',true).first();
23482 * Protected method that will not generally be called directly. It
23483 * is called when the editor creates its toolbar. Override this method if you need to
23484 * add custom toolbar buttons.
23485 * @param {HtmlEditor} editor
23487 createToolbar : function(){
23488 Roo.log('renewing');
23489 Roo.log("create toolbars");
23491 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23492 this.toolbars[0].render(this.toolbarContainer());
23496 // if (!editor.toolbars || !editor.toolbars.length) {
23497 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23500 // for (var i =0 ; i < editor.toolbars.length;i++) {
23501 // editor.toolbars[i] = Roo.factory(
23502 // typeof(editor.toolbars[i]) == 'string' ?
23503 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23504 // Roo.bootstrap.HtmlEditor);
23505 // editor.toolbars[i].init(editor);
23511 onRender : function(ct, position)
23513 // Roo.log("Call onRender: " + this.xtype);
23515 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23517 this.wrap = this.inputEl().wrap({
23518 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23521 this.editorcore.onRender(ct, position);
23523 if (this.resizable) {
23524 this.resizeEl = new Roo.Resizable(this.wrap, {
23528 minHeight : this.height,
23529 height: this.height,
23530 handles : this.resizable,
23533 resize : function(r, w, h) {
23534 _t.onResize(w,h); // -something
23540 this.createToolbar(this);
23543 if(!this.width && this.resizable){
23544 this.setSize(this.wrap.getSize());
23546 if (this.resizeEl) {
23547 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23548 // should trigger onReize..
23554 onResize : function(w, h)
23556 Roo.log('resize: ' +w + ',' + h );
23557 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23561 if(this.inputEl() ){
23562 if(typeof w == 'number'){
23563 var aw = w - this.wrap.getFrameWidth('lr');
23564 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23567 if(typeof h == 'number'){
23568 var tbh = -11; // fixme it needs to tool bar size!
23569 for (var i =0; i < this.toolbars.length;i++) {
23570 // fixme - ask toolbars for heights?
23571 tbh += this.toolbars[i].el.getHeight();
23572 //if (this.toolbars[i].footer) {
23573 // tbh += this.toolbars[i].footer.el.getHeight();
23581 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23582 ah -= 5; // knock a few pixes off for look..
23583 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23587 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23588 this.editorcore.onResize(ew,eh);
23593 * Toggles the editor between standard and source edit mode.
23594 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23596 toggleSourceEdit : function(sourceEditMode)
23598 this.editorcore.toggleSourceEdit(sourceEditMode);
23600 if(this.editorcore.sourceEditMode){
23601 Roo.log('editor - showing textarea');
23604 // Roo.log(this.syncValue());
23606 this.inputEl().removeClass(['hide', 'x-hidden']);
23607 this.inputEl().dom.removeAttribute('tabIndex');
23608 this.inputEl().focus();
23610 Roo.log('editor - hiding textarea');
23612 // Roo.log(this.pushValue());
23615 this.inputEl().addClass(['hide', 'x-hidden']);
23616 this.inputEl().dom.setAttribute('tabIndex', -1);
23617 //this.deferFocus();
23620 if(this.resizable){
23621 this.setSize(this.wrap.getSize());
23624 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23627 // private (for BoxComponent)
23628 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23630 // private (for BoxComponent)
23631 getResizeEl : function(){
23635 // private (for BoxComponent)
23636 getPositionEl : function(){
23641 initEvents : function(){
23642 this.originalValue = this.getValue();
23646 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23649 // markInvalid : Roo.emptyFn,
23651 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23654 // clearInvalid : Roo.emptyFn,
23656 setValue : function(v){
23657 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23658 this.editorcore.pushValue();
23663 deferFocus : function(){
23664 this.focus.defer(10, this);
23668 focus : function(){
23669 this.editorcore.focus();
23675 onDestroy : function(){
23681 for (var i =0; i < this.toolbars.length;i++) {
23682 // fixme - ask toolbars for heights?
23683 this.toolbars[i].onDestroy();
23686 this.wrap.dom.innerHTML = '';
23687 this.wrap.remove();
23692 onFirstFocus : function(){
23693 //Roo.log("onFirstFocus");
23694 this.editorcore.onFirstFocus();
23695 for (var i =0; i < this.toolbars.length;i++) {
23696 this.toolbars[i].onFirstFocus();
23702 syncValue : function()
23704 this.editorcore.syncValue();
23707 pushValue : function()
23709 this.editorcore.pushValue();
23713 // hide stuff that is not compatible
23727 * @event specialkey
23731 * @cfg {String} fieldClass @hide
23734 * @cfg {String} focusClass @hide
23737 * @cfg {String} autoCreate @hide
23740 * @cfg {String} inputType @hide
23743 * @cfg {String} invalidClass @hide
23746 * @cfg {String} invalidText @hide
23749 * @cfg {String} msgFx @hide
23752 * @cfg {String} validateOnBlur @hide
23761 Roo.namespace('Roo.bootstrap.htmleditor');
23763 * @class Roo.bootstrap.HtmlEditorToolbar1
23768 new Roo.bootstrap.HtmlEditor({
23771 new Roo.bootstrap.HtmlEditorToolbar1({
23772 disable : { fonts: 1 , format: 1, ..., ... , ...],
23778 * @cfg {Object} disable List of elements to disable..
23779 * @cfg {Array} btns List of additional buttons.
23783 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23786 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23789 Roo.apply(this, config);
23791 // default disabled, based on 'good practice'..
23792 this.disable = this.disable || {};
23793 Roo.applyIf(this.disable, {
23796 specialElements : true
23798 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23800 this.editor = config.editor;
23801 this.editorcore = config.editor.editorcore;
23803 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23805 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23806 // dont call parent... till later.
23808 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23813 editorcore : false,
23818 "h1","h2","h3","h4","h5","h6",
23820 "abbr", "acronym", "address", "cite", "samp", "var",
23824 onRender : function(ct, position)
23826 // Roo.log("Call onRender: " + this.xtype);
23828 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23830 this.el.dom.style.marginBottom = '0';
23832 var editorcore = this.editorcore;
23833 var editor= this.editor;
23836 var btn = function(id,cmd , toggle, handler, html){
23838 var event = toggle ? 'toggle' : 'click';
23843 xns: Roo.bootstrap,
23846 enableToggle:toggle !== false,
23848 pressed : toggle ? false : null,
23851 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23852 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23858 // var cb_box = function...
23863 xns: Roo.bootstrap,
23864 glyphicon : 'font',
23868 xns: Roo.bootstrap,
23872 Roo.each(this.formats, function(f) {
23873 style.menu.items.push({
23875 xns: Roo.bootstrap,
23876 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23881 editorcore.insertTag(this.tagname);
23888 children.push(style);
23890 btn('bold',false,true);
23891 btn('italic',false,true);
23892 btn('align-left', 'justifyleft',true);
23893 btn('align-center', 'justifycenter',true);
23894 btn('align-right' , 'justifyright',true);
23895 btn('link', false, false, function(btn) {
23896 //Roo.log("create link?");
23897 var url = prompt(this.createLinkText, this.defaultLinkValue);
23898 if(url && url != 'http:/'+'/'){
23899 this.editorcore.relayCmd('createlink', url);
23902 btn('list','insertunorderedlist',true);
23903 btn('pencil', false,true, function(btn){
23905 this.toggleSourceEdit(btn.pressed);
23908 if (this.editor.btns.length > 0) {
23909 for (var i = 0; i<this.editor.btns.length; i++) {
23910 children.push(this.editor.btns[i]);
23918 xns: Roo.bootstrap,
23923 xns: Roo.bootstrap,
23928 cog.menu.items.push({
23930 xns: Roo.bootstrap,
23931 html : Clean styles,
23936 editorcore.insertTag(this.tagname);
23945 this.xtype = 'NavSimplebar';
23947 for(var i=0;i< children.length;i++) {
23949 this.buttons.add(this.addxtypeChild(children[i]));
23953 editor.on('editorevent', this.updateToolbar, this);
23955 onBtnClick : function(id)
23957 this.editorcore.relayCmd(id);
23958 this.editorcore.focus();
23962 * Protected method that will not generally be called directly. It triggers
23963 * a toolbar update by reading the markup state of the current selection in the editor.
23965 updateToolbar: function(){
23967 if(!this.editorcore.activated){
23968 this.editor.onFirstFocus(); // is this neeed?
23972 var btns = this.buttons;
23973 var doc = this.editorcore.doc;
23974 btns.get('bold').setActive(doc.queryCommandState('bold'));
23975 btns.get('italic').setActive(doc.queryCommandState('italic'));
23976 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23978 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23979 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23980 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23982 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23983 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23986 var ans = this.editorcore.getAllAncestors();
23987 if (this.formatCombo) {
23990 var store = this.formatCombo.store;
23991 this.formatCombo.setValue("");
23992 for (var i =0; i < ans.length;i++) {
23993 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23995 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24003 // hides menus... - so this cant be on a menu...
24004 Roo.bootstrap.MenuMgr.hideAll();
24006 Roo.bootstrap.MenuMgr.hideAll();
24007 //this.editorsyncValue();
24009 onFirstFocus: function() {
24010 this.buttons.each(function(item){
24014 toggleSourceEdit : function(sourceEditMode){
24017 if(sourceEditMode){
24018 Roo.log("disabling buttons");
24019 this.buttons.each( function(item){
24020 if(item.cmd != 'pencil'){
24026 Roo.log("enabling buttons");
24027 if(this.editorcore.initialized){
24028 this.buttons.each( function(item){
24034 Roo.log("calling toggole on editor");
24035 // tell the editor that it's been pressed..
24036 this.editor.toggleSourceEdit(sourceEditMode);
24046 * @class Roo.bootstrap.Table.AbstractSelectionModel
24047 * @extends Roo.util.Observable
24048 * Abstract base class for grid SelectionModels. It provides the interface that should be
24049 * implemented by descendant classes. This class should not be directly instantiated.
24052 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24053 this.locked = false;
24054 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24058 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24059 /** @ignore Called by the grid automatically. Do not call directly. */
24060 init : function(grid){
24066 * Locks the selections.
24069 this.locked = true;
24073 * Unlocks the selections.
24075 unlock : function(){
24076 this.locked = false;
24080 * Returns true if the selections are locked.
24081 * @return {Boolean}
24083 isLocked : function(){
24084 return this.locked;
24088 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24089 * @class Roo.bootstrap.Table.RowSelectionModel
24090 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24091 * It supports multiple selections and keyboard selection/navigation.
24093 * @param {Object} config
24096 Roo.bootstrap.Table.RowSelectionModel = function(config){
24097 Roo.apply(this, config);
24098 this.selections = new Roo.util.MixedCollection(false, function(o){
24103 this.lastActive = false;
24107 * @event selectionchange
24108 * Fires when the selection changes
24109 * @param {SelectionModel} this
24111 "selectionchange" : true,
24113 * @event afterselectionchange
24114 * Fires after the selection changes (eg. by key press or clicking)
24115 * @param {SelectionModel} this
24117 "afterselectionchange" : true,
24119 * @event beforerowselect
24120 * Fires when a row is selected being selected, return false to cancel.
24121 * @param {SelectionModel} this
24122 * @param {Number} rowIndex The selected index
24123 * @param {Boolean} keepExisting False if other selections will be cleared
24125 "beforerowselect" : true,
24128 * Fires when a row is selected.
24129 * @param {SelectionModel} this
24130 * @param {Number} rowIndex The selected index
24131 * @param {Roo.data.Record} r The record
24133 "rowselect" : true,
24135 * @event rowdeselect
24136 * Fires when a row is deselected.
24137 * @param {SelectionModel} this
24138 * @param {Number} rowIndex The selected index
24140 "rowdeselect" : true
24142 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24143 this.locked = false;
24146 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24148 * @cfg {Boolean} singleSelect
24149 * True to allow selection of only one row at a time (defaults to false)
24151 singleSelect : false,
24154 initEvents : function()
24157 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24158 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24159 //}else{ // allow click to work like normal
24160 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24162 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24163 this.grid.on("rowclick", this.handleMouseDown, this);
24165 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24166 "up" : function(e){
24168 this.selectPrevious(e.shiftKey);
24169 }else if(this.last !== false && this.lastActive !== false){
24170 var last = this.last;
24171 this.selectRange(this.last, this.lastActive-1);
24172 this.grid.getView().focusRow(this.lastActive);
24173 if(last !== false){
24177 this.selectFirstRow();
24179 this.fireEvent("afterselectionchange", this);
24181 "down" : function(e){
24183 this.selectNext(e.shiftKey);
24184 }else if(this.last !== false && this.lastActive !== false){
24185 var last = this.last;
24186 this.selectRange(this.last, this.lastActive+1);
24187 this.grid.getView().focusRow(this.lastActive);
24188 if(last !== false){
24192 this.selectFirstRow();
24194 this.fireEvent("afterselectionchange", this);
24198 this.grid.store.on('load', function(){
24199 this.selections.clear();
24202 var view = this.grid.view;
24203 view.on("refresh", this.onRefresh, this);
24204 view.on("rowupdated", this.onRowUpdated, this);
24205 view.on("rowremoved", this.onRemove, this);
24210 onRefresh : function()
24212 var ds = this.grid.store, i, v = this.grid.view;
24213 var s = this.selections;
24214 s.each(function(r){
24215 if((i = ds.indexOfId(r.id)) != -1){
24224 onRemove : function(v, index, r){
24225 this.selections.remove(r);
24229 onRowUpdated : function(v, index, r){
24230 if(this.isSelected(r)){
24231 v.onRowSelect(index);
24237 * @param {Array} records The records to select
24238 * @param {Boolean} keepExisting (optional) True to keep existing selections
24240 selectRecords : function(records, keepExisting)
24243 this.clearSelections();
24245 var ds = this.grid.store;
24246 for(var i = 0, len = records.length; i < len; i++){
24247 this.selectRow(ds.indexOf(records[i]), true);
24252 * Gets the number of selected rows.
24255 getCount : function(){
24256 return this.selections.length;
24260 * Selects the first row in the grid.
24262 selectFirstRow : function(){
24267 * Select the last row.
24268 * @param {Boolean} keepExisting (optional) True to keep existing selections
24270 selectLastRow : function(keepExisting){
24271 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24272 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24276 * Selects the row immediately following the last selected row.
24277 * @param {Boolean} keepExisting (optional) True to keep existing selections
24279 selectNext : function(keepExisting)
24281 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24282 this.selectRow(this.last+1, keepExisting);
24283 this.grid.getView().focusRow(this.last);
24288 * Selects the row that precedes the last selected row.
24289 * @param {Boolean} keepExisting (optional) True to keep existing selections
24291 selectPrevious : function(keepExisting){
24293 this.selectRow(this.last-1, keepExisting);
24294 this.grid.getView().focusRow(this.last);
24299 * Returns the selected records
24300 * @return {Array} Array of selected records
24302 getSelections : function(){
24303 return [].concat(this.selections.items);
24307 * Returns the first selected record.
24310 getSelected : function(){
24311 return this.selections.itemAt(0);
24316 * Clears all selections.
24318 clearSelections : function(fast)
24324 var ds = this.grid.store;
24325 var s = this.selections;
24326 s.each(function(r){
24327 this.deselectRow(ds.indexOfId(r.id));
24331 this.selections.clear();
24338 * Selects all rows.
24340 selectAll : function(){
24344 this.selections.clear();
24345 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24346 this.selectRow(i, true);
24351 * Returns True if there is a selection.
24352 * @return {Boolean}
24354 hasSelection : function(){
24355 return this.selections.length > 0;
24359 * Returns True if the specified row is selected.
24360 * @param {Number/Record} record The record or index of the record to check
24361 * @return {Boolean}
24363 isSelected : function(index){
24364 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24365 return (r && this.selections.key(r.id) ? true : false);
24369 * Returns True if the specified record id is selected.
24370 * @param {String} id The id of record to check
24371 * @return {Boolean}
24373 isIdSelected : function(id){
24374 return (this.selections.key(id) ? true : false);
24379 handleMouseDBClick : function(e, t){
24383 handleMouseDown : function(e, t)
24385 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24386 if(this.isLocked() || rowIndex < 0 ){
24389 if(e.shiftKey && this.last !== false){
24390 var last = this.last;
24391 this.selectRange(last, rowIndex, e.ctrlKey);
24392 this.last = last; // reset the last
24396 var isSelected = this.isSelected(rowIndex);
24397 //Roo.log("select row:" + rowIndex);
24399 this.deselectRow(rowIndex);
24401 this.selectRow(rowIndex, true);
24405 if(e.button !== 0 && isSelected){
24406 alert('rowIndex 2: ' + rowIndex);
24407 view.focusRow(rowIndex);
24408 }else if(e.ctrlKey && isSelected){
24409 this.deselectRow(rowIndex);
24410 }else if(!isSelected){
24411 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24412 view.focusRow(rowIndex);
24416 this.fireEvent("afterselectionchange", this);
24419 handleDragableRowClick : function(grid, rowIndex, e)
24421 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24422 this.selectRow(rowIndex, false);
24423 grid.view.focusRow(rowIndex);
24424 this.fireEvent("afterselectionchange", this);
24429 * Selects multiple rows.
24430 * @param {Array} rows Array of the indexes of the row to select
24431 * @param {Boolean} keepExisting (optional) True to keep existing selections
24433 selectRows : function(rows, keepExisting){
24435 this.clearSelections();
24437 for(var i = 0, len = rows.length; i < len; i++){
24438 this.selectRow(rows[i], true);
24443 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24444 * @param {Number} startRow The index of the first row in the range
24445 * @param {Number} endRow The index of the last row in the range
24446 * @param {Boolean} keepExisting (optional) True to retain existing selections
24448 selectRange : function(startRow, endRow, keepExisting){
24453 this.clearSelections();
24455 if(startRow <= endRow){
24456 for(var i = startRow; i <= endRow; i++){
24457 this.selectRow(i, true);
24460 for(var i = startRow; i >= endRow; i--){
24461 this.selectRow(i, true);
24467 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24468 * @param {Number} startRow The index of the first row in the range
24469 * @param {Number} endRow The index of the last row in the range
24471 deselectRange : function(startRow, endRow, preventViewNotify){
24475 for(var i = startRow; i <= endRow; i++){
24476 this.deselectRow(i, preventViewNotify);
24482 * @param {Number} row The index of the row to select
24483 * @param {Boolean} keepExisting (optional) True to keep existing selections
24485 selectRow : function(index, keepExisting, preventViewNotify)
24487 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24490 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24491 if(!keepExisting || this.singleSelect){
24492 this.clearSelections();
24495 var r = this.grid.store.getAt(index);
24496 //console.log('selectRow - record id :' + r.id);
24498 this.selections.add(r);
24499 this.last = this.lastActive = index;
24500 if(!preventViewNotify){
24501 var proxy = new Roo.Element(
24502 this.grid.getRowDom(index)
24504 proxy.addClass('bg-info info');
24506 this.fireEvent("rowselect", this, index, r);
24507 this.fireEvent("selectionchange", this);
24513 * @param {Number} row The index of the row to deselect
24515 deselectRow : function(index, preventViewNotify)
24520 if(this.last == index){
24523 if(this.lastActive == index){
24524 this.lastActive = false;
24527 var r = this.grid.store.getAt(index);
24532 this.selections.remove(r);
24533 //.console.log('deselectRow - record id :' + r.id);
24534 if(!preventViewNotify){
24536 var proxy = new Roo.Element(
24537 this.grid.getRowDom(index)
24539 proxy.removeClass('bg-info info');
24541 this.fireEvent("rowdeselect", this, index);
24542 this.fireEvent("selectionchange", this);
24546 restoreLast : function(){
24548 this.last = this._last;
24553 acceptsNav : function(row, col, cm){
24554 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24558 onEditorKey : function(field, e){
24559 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24564 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24566 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24568 }else if(k == e.ENTER && !e.ctrlKey){
24572 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24574 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24576 }else if(k == e.ESC){
24580 g.startEditing(newCell[0], newCell[1]);
24586 * Ext JS Library 1.1.1
24587 * Copyright(c) 2006-2007, Ext JS, LLC.
24589 * Originally Released Under LGPL - original licence link has changed is not relivant.
24592 * <script type="text/javascript">
24596 * @class Roo.bootstrap.PagingToolbar
24597 * @extends Roo.bootstrap.NavSimplebar
24598 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24600 * Create a new PagingToolbar
24601 * @param {Object} config The config object
24602 * @param {Roo.data.Store} store
24604 Roo.bootstrap.PagingToolbar = function(config)
24606 // old args format still supported... - xtype is prefered..
24607 // created from xtype...
24609 this.ds = config.dataSource;
24611 if (config.store && !this.ds) {
24612 this.store= Roo.factory(config.store, Roo.data);
24613 this.ds = this.store;
24614 this.ds.xmodule = this.xmodule || false;
24617 this.toolbarItems = [];
24618 if (config.items) {
24619 this.toolbarItems = config.items;
24622 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24627 this.bind(this.ds);
24630 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24634 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24636 * @cfg {Roo.data.Store} dataSource
24637 * The underlying data store providing the paged data
24640 * @cfg {String/HTMLElement/Element} container
24641 * container The id or element that will contain the toolbar
24644 * @cfg {Boolean} displayInfo
24645 * True to display the displayMsg (defaults to false)
24648 * @cfg {Number} pageSize
24649 * The number of records to display per page (defaults to 20)
24653 * @cfg {String} displayMsg
24654 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24656 displayMsg : 'Displaying {0} - {1} of {2}',
24658 * @cfg {String} emptyMsg
24659 * The message to display when no records are found (defaults to "No data to display")
24661 emptyMsg : 'No data to display',
24663 * Customizable piece of the default paging text (defaults to "Page")
24666 beforePageText : "Page",
24668 * Customizable piece of the default paging text (defaults to "of %0")
24671 afterPageText : "of {0}",
24673 * Customizable piece of the default paging text (defaults to "First Page")
24676 firstText : "First Page",
24678 * Customizable piece of the default paging text (defaults to "Previous Page")
24681 prevText : "Previous Page",
24683 * Customizable piece of the default paging text (defaults to "Next Page")
24686 nextText : "Next Page",
24688 * Customizable piece of the default paging text (defaults to "Last Page")
24691 lastText : "Last Page",
24693 * Customizable piece of the default paging text (defaults to "Refresh")
24696 refreshText : "Refresh",
24700 onRender : function(ct, position)
24702 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24703 this.navgroup.parentId = this.id;
24704 this.navgroup.onRender(this.el, null);
24705 // add the buttons to the navgroup
24707 if(this.displayInfo){
24708 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24709 this.displayEl = this.el.select('.x-paging-info', true).first();
24710 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24711 // this.displayEl = navel.el.select('span',true).first();
24717 Roo.each(_this.buttons, function(e){ // this might need to use render????
24718 Roo.factory(e).render(_this.el);
24722 Roo.each(_this.toolbarItems, function(e) {
24723 _this.navgroup.addItem(e);
24727 this.first = this.navgroup.addItem({
24728 tooltip: this.firstText,
24730 icon : 'fa fa-backward',
24732 preventDefault: true,
24733 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24736 this.prev = this.navgroup.addItem({
24737 tooltip: this.prevText,
24739 icon : 'fa fa-step-backward',
24741 preventDefault: true,
24742 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24744 //this.addSeparator();
24747 var field = this.navgroup.addItem( {
24749 cls : 'x-paging-position',
24751 html : this.beforePageText +
24752 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24753 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24756 this.field = field.el.select('input', true).first();
24757 this.field.on("keydown", this.onPagingKeydown, this);
24758 this.field.on("focus", function(){this.dom.select();});
24761 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24762 //this.field.setHeight(18);
24763 //this.addSeparator();
24764 this.next = this.navgroup.addItem({
24765 tooltip: this.nextText,
24767 html : ' <i class="fa fa-step-forward">',
24769 preventDefault: true,
24770 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24772 this.last = this.navgroup.addItem({
24773 tooltip: this.lastText,
24774 icon : 'fa fa-forward',
24777 preventDefault: true,
24778 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24780 //this.addSeparator();
24781 this.loading = this.navgroup.addItem({
24782 tooltip: this.refreshText,
24783 icon: 'fa fa-refresh',
24784 preventDefault: true,
24785 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24791 updateInfo : function(){
24792 if(this.displayEl){
24793 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24794 var msg = count == 0 ?
24798 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24800 this.displayEl.update(msg);
24805 onLoad : function(ds, r, o)
24807 this.cursor = o.params.start ? o.params.start : 0;
24809 var d = this.getPageData(),
24814 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24815 this.field.dom.value = ap;
24816 this.first.setDisabled(ap == 1);
24817 this.prev.setDisabled(ap == 1);
24818 this.next.setDisabled(ap == ps);
24819 this.last.setDisabled(ap == ps);
24820 this.loading.enable();
24825 getPageData : function(){
24826 var total = this.ds.getTotalCount();
24829 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24830 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24835 onLoadError : function(){
24836 this.loading.enable();
24840 onPagingKeydown : function(e){
24841 var k = e.getKey();
24842 var d = this.getPageData();
24844 var v = this.field.dom.value, pageNum;
24845 if(!v || isNaN(pageNum = parseInt(v, 10))){
24846 this.field.dom.value = d.activePage;
24849 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24850 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24853 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))
24855 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24856 this.field.dom.value = pageNum;
24857 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24860 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24862 var v = this.field.dom.value, pageNum;
24863 var increment = (e.shiftKey) ? 10 : 1;
24864 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24867 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24868 this.field.dom.value = d.activePage;
24871 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24873 this.field.dom.value = parseInt(v, 10) + increment;
24874 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24875 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24882 beforeLoad : function(){
24884 this.loading.disable();
24889 onClick : function(which){
24898 ds.load({params:{start: 0, limit: this.pageSize}});
24901 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24904 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24907 var total = ds.getTotalCount();
24908 var extra = total % this.pageSize;
24909 var lastStart = extra ? (total - extra) : total-this.pageSize;
24910 ds.load({params:{start: lastStart, limit: this.pageSize}});
24913 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24919 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24920 * @param {Roo.data.Store} store The data store to unbind
24922 unbind : function(ds){
24923 ds.un("beforeload", this.beforeLoad, this);
24924 ds.un("load", this.onLoad, this);
24925 ds.un("loadexception", this.onLoadError, this);
24926 ds.un("remove", this.updateInfo, this);
24927 ds.un("add", this.updateInfo, this);
24928 this.ds = undefined;
24932 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24933 * @param {Roo.data.Store} store The data store to bind
24935 bind : function(ds){
24936 ds.on("beforeload", this.beforeLoad, this);
24937 ds.on("load", this.onLoad, this);
24938 ds.on("loadexception", this.onLoadError, this);
24939 ds.on("remove", this.updateInfo, this);
24940 ds.on("add", this.updateInfo, this);
24951 * @class Roo.bootstrap.MessageBar
24952 * @extends Roo.bootstrap.Component
24953 * Bootstrap MessageBar class
24954 * @cfg {String} html contents of the MessageBar
24955 * @cfg {String} weight (info | success | warning | danger) default info
24956 * @cfg {String} beforeClass insert the bar before the given class
24957 * @cfg {Boolean} closable (true | false) default false
24958 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24961 * Create a new Element
24962 * @param {Object} config The config object
24965 Roo.bootstrap.MessageBar = function(config){
24966 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24969 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24975 beforeClass: 'bootstrap-sticky-wrap',
24977 getAutoCreate : function(){
24981 cls: 'alert alert-dismissable alert-' + this.weight,
24986 html: this.html || ''
24992 cfg.cls += ' alert-messages-fixed';
25006 onRender : function(ct, position)
25008 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25011 var cfg = Roo.apply({}, this.getAutoCreate());
25015 cfg.cls += ' ' + this.cls;
25018 cfg.style = this.style;
25020 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25022 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25025 this.el.select('>button.close').on('click', this.hide, this);
25031 if (!this.rendered) {
25037 this.fireEvent('show', this);
25043 if (!this.rendered) {
25049 this.fireEvent('hide', this);
25052 update : function()
25054 // var e = this.el.dom.firstChild;
25056 // if(this.closable){
25057 // e = e.nextSibling;
25060 // e.data = this.html || '';
25062 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25078 * @class Roo.bootstrap.Graph
25079 * @extends Roo.bootstrap.Component
25080 * Bootstrap Graph class
25084 @cfg {String} graphtype bar | vbar | pie
25085 @cfg {number} g_x coodinator | centre x (pie)
25086 @cfg {number} g_y coodinator | centre y (pie)
25087 @cfg {number} g_r radius (pie)
25088 @cfg {number} g_height height of the chart (respected by all elements in the set)
25089 @cfg {number} g_width width of the chart (respected by all elements in the set)
25090 @cfg {Object} title The title of the chart
25093 -opts (object) options for the chart
25095 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25096 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25098 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.
25099 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25101 o stretch (boolean)
25103 -opts (object) options for the pie
25106 o startAngle (number)
25107 o endAngle (number)
25111 * Create a new Input
25112 * @param {Object} config The config object
25115 Roo.bootstrap.Graph = function(config){
25116 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25122 * The img click event for the img.
25123 * @param {Roo.EventObject} e
25129 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25140 //g_colors: this.colors,
25147 getAutoCreate : function(){
25158 onRender : function(ct,position){
25161 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25163 if (typeof(Raphael) == 'undefined') {
25164 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25168 this.raphael = Raphael(this.el.dom);
25170 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25171 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25172 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25173 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25175 r.text(160, 10, "Single Series Chart").attr(txtattr);
25176 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25177 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25178 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25180 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25181 r.barchart(330, 10, 300, 220, data1);
25182 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25183 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25186 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25187 // r.barchart(30, 30, 560, 250, xdata, {
25188 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25189 // axis : "0 0 1 1",
25190 // axisxlabels : xdata
25191 // //yvalues : cols,
25194 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25196 // this.load(null,xdata,{
25197 // axis : "0 0 1 1",
25198 // axisxlabels : xdata
25203 load : function(graphtype,xdata,opts)
25205 this.raphael.clear();
25207 graphtype = this.graphtype;
25212 var r = this.raphael,
25213 fin = function () {
25214 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25216 fout = function () {
25217 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25219 pfin = function() {
25220 this.sector.stop();
25221 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25224 this.label[0].stop();
25225 this.label[0].attr({ r: 7.5 });
25226 this.label[1].attr({ "font-weight": 800 });
25229 pfout = function() {
25230 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25233 this.label[0].animate({ r: 5 }, 500, "bounce");
25234 this.label[1].attr({ "font-weight": 400 });
25240 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25243 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25246 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25247 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25249 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25256 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25261 setTitle: function(o)
25266 initEvents: function() {
25269 this.el.on('click', this.onClick, this);
25273 onClick : function(e)
25275 Roo.log('img onclick');
25276 this.fireEvent('click', this, e);
25288 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25291 * @class Roo.bootstrap.dash.NumberBox
25292 * @extends Roo.bootstrap.Component
25293 * Bootstrap NumberBox class
25294 * @cfg {String} headline Box headline
25295 * @cfg {String} content Box content
25296 * @cfg {String} icon Box icon
25297 * @cfg {String} footer Footer text
25298 * @cfg {String} fhref Footer href
25301 * Create a new NumberBox
25302 * @param {Object} config The config object
25306 Roo.bootstrap.dash.NumberBox = function(config){
25307 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25311 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25320 getAutoCreate : function(){
25324 cls : 'small-box ',
25332 cls : 'roo-headline',
25333 html : this.headline
25337 cls : 'roo-content',
25338 html : this.content
25352 cls : 'ion ' + this.icon
25361 cls : 'small-box-footer',
25362 href : this.fhref || '#',
25366 cfg.cn.push(footer);
25373 onRender : function(ct,position){
25374 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25381 setHeadline: function (value)
25383 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25386 setFooter: function (value, href)
25388 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25391 this.el.select('a.small-box-footer',true).first().attr('href', href);
25396 setContent: function (value)
25398 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25401 initEvents: function()
25415 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25418 * @class Roo.bootstrap.dash.TabBox
25419 * @extends Roo.bootstrap.Component
25420 * Bootstrap TabBox class
25421 * @cfg {String} title Title of the TabBox
25422 * @cfg {String} icon Icon of the TabBox
25423 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25424 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25427 * Create a new TabBox
25428 * @param {Object} config The config object
25432 Roo.bootstrap.dash.TabBox = function(config){
25433 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25438 * When a pane is added
25439 * @param {Roo.bootstrap.dash.TabPane} pane
25443 * @event activatepane
25444 * When a pane is activated
25445 * @param {Roo.bootstrap.dash.TabPane} pane
25447 "activatepane" : true
25455 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25460 tabScrollable : false,
25462 getChildContainer : function()
25464 return this.el.select('.tab-content', true).first();
25467 getAutoCreate : function(){
25471 cls: 'pull-left header',
25479 cls: 'fa ' + this.icon
25485 cls: 'nav nav-tabs pull-right',
25491 if(this.tabScrollable){
25498 cls: 'nav nav-tabs pull-right',
25509 cls: 'nav-tabs-custom',
25514 cls: 'tab-content no-padding',
25522 initEvents : function()
25524 //Roo.log('add add pane handler');
25525 this.on('addpane', this.onAddPane, this);
25528 * Updates the box title
25529 * @param {String} html to set the title to.
25531 setTitle : function(value)
25533 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25535 onAddPane : function(pane)
25537 this.panes.push(pane);
25538 //Roo.log('addpane');
25540 // tabs are rendere left to right..
25541 if(!this.showtabs){
25545 var ctr = this.el.select('.nav-tabs', true).first();
25548 var existing = ctr.select('.nav-tab',true);
25549 var qty = existing.getCount();;
25552 var tab = ctr.createChild({
25554 cls : 'nav-tab' + (qty ? '' : ' active'),
25562 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25565 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25567 pane.el.addClass('active');
25572 onTabClick : function(ev,un,ob,pane)
25574 //Roo.log('tab - prev default');
25575 ev.preventDefault();
25578 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25579 pane.tab.addClass('active');
25580 //Roo.log(pane.title);
25581 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25582 // technically we should have a deactivate event.. but maybe add later.
25583 // and it should not de-activate the selected tab...
25584 this.fireEvent('activatepane', pane);
25585 pane.el.addClass('active');
25586 pane.fireEvent('activate');
25591 getActivePane : function()
25594 Roo.each(this.panes, function(p) {
25595 if(p.el.hasClass('active')){
25616 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25618 * @class Roo.bootstrap.TabPane
25619 * @extends Roo.bootstrap.Component
25620 * Bootstrap TabPane class
25621 * @cfg {Boolean} active (false | true) Default false
25622 * @cfg {String} title title of panel
25626 * Create a new TabPane
25627 * @param {Object} config The config object
25630 Roo.bootstrap.dash.TabPane = function(config){
25631 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25637 * When a pane is activated
25638 * @param {Roo.bootstrap.dash.TabPane} pane
25645 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25650 // the tabBox that this is attached to.
25653 getAutoCreate : function()
25661 cfg.cls += ' active';
25666 initEvents : function()
25668 //Roo.log('trigger add pane handler');
25669 this.parent().fireEvent('addpane', this)
25673 * Updates the tab title
25674 * @param {String} html to set the title to.
25676 setTitle: function(str)
25682 this.tab.select('a', true).first().dom.innerHTML = str;
25699 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25702 * @class Roo.bootstrap.menu.Menu
25703 * @extends Roo.bootstrap.Component
25704 * Bootstrap Menu class - container for Menu
25705 * @cfg {String} html Text of the menu
25706 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25707 * @cfg {String} icon Font awesome icon
25708 * @cfg {String} pos Menu align to (top | bottom) default bottom
25712 * Create a new Menu
25713 * @param {Object} config The config object
25717 Roo.bootstrap.menu.Menu = function(config){
25718 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25722 * @event beforeshow
25723 * Fires before this menu is displayed
25724 * @param {Roo.bootstrap.menu.Menu} this
25728 * @event beforehide
25729 * Fires before this menu is hidden
25730 * @param {Roo.bootstrap.menu.Menu} this
25735 * Fires after this menu is displayed
25736 * @param {Roo.bootstrap.menu.Menu} this
25741 * Fires after this menu is hidden
25742 * @param {Roo.bootstrap.menu.Menu} this
25747 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25748 * @param {Roo.bootstrap.menu.Menu} this
25749 * @param {Roo.EventObject} e
25756 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25760 weight : 'default',
25765 getChildContainer : function() {
25766 if(this.isSubMenu){
25770 return this.el.select('ul.dropdown-menu', true).first();
25773 getAutoCreate : function()
25778 cls : 'roo-menu-text',
25786 cls : 'fa ' + this.icon
25797 cls : 'dropdown-button btn btn-' + this.weight,
25802 cls : 'dropdown-toggle btn btn-' + this.weight,
25812 cls : 'dropdown-menu'
25818 if(this.pos == 'top'){
25819 cfg.cls += ' dropup';
25822 if(this.isSubMenu){
25825 cls : 'dropdown-menu'
25832 onRender : function(ct, position)
25834 this.isSubMenu = ct.hasClass('dropdown-submenu');
25836 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25839 initEvents : function()
25841 if(this.isSubMenu){
25845 this.hidden = true;
25847 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25848 this.triggerEl.on('click', this.onTriggerPress, this);
25850 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25851 this.buttonEl.on('click', this.onClick, this);
25857 if(this.isSubMenu){
25861 return this.el.select('ul.dropdown-menu', true).first();
25864 onClick : function(e)
25866 this.fireEvent("click", this, e);
25869 onTriggerPress : function(e)
25871 if (this.isVisible()) {
25878 isVisible : function(){
25879 return !this.hidden;
25884 this.fireEvent("beforeshow", this);
25886 this.hidden = false;
25887 this.el.addClass('open');
25889 Roo.get(document).on("mouseup", this.onMouseUp, this);
25891 this.fireEvent("show", this);
25898 this.fireEvent("beforehide", this);
25900 this.hidden = true;
25901 this.el.removeClass('open');
25903 Roo.get(document).un("mouseup", this.onMouseUp);
25905 this.fireEvent("hide", this);
25908 onMouseUp : function()
25922 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25925 * @class Roo.bootstrap.menu.Item
25926 * @extends Roo.bootstrap.Component
25927 * Bootstrap MenuItem class
25928 * @cfg {Boolean} submenu (true | false) default false
25929 * @cfg {String} html text of the item
25930 * @cfg {String} href the link
25931 * @cfg {Boolean} disable (true | false) default false
25932 * @cfg {Boolean} preventDefault (true | false) default true
25933 * @cfg {String} icon Font awesome icon
25934 * @cfg {String} pos Submenu align to (left | right) default right
25938 * Create a new Item
25939 * @param {Object} config The config object
25943 Roo.bootstrap.menu.Item = function(config){
25944 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25948 * Fires when the mouse is hovering over this menu
25949 * @param {Roo.bootstrap.menu.Item} this
25950 * @param {Roo.EventObject} e
25955 * Fires when the mouse exits this menu
25956 * @param {Roo.bootstrap.menu.Item} this
25957 * @param {Roo.EventObject} e
25963 * The raw click event for the entire grid.
25964 * @param {Roo.EventObject} e
25970 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25975 preventDefault: true,
25980 getAutoCreate : function()
25985 cls : 'roo-menu-item-text',
25993 cls : 'fa ' + this.icon
26002 href : this.href || '#',
26009 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26013 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26015 if(this.pos == 'left'){
26016 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26023 initEvents : function()
26025 this.el.on('mouseover', this.onMouseOver, this);
26026 this.el.on('mouseout', this.onMouseOut, this);
26028 this.el.select('a', true).first().on('click', this.onClick, this);
26032 onClick : function(e)
26034 if(this.preventDefault){
26035 e.preventDefault();
26038 this.fireEvent("click", this, e);
26041 onMouseOver : function(e)
26043 if(this.submenu && this.pos == 'left'){
26044 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26047 this.fireEvent("mouseover", this, e);
26050 onMouseOut : function(e)
26052 this.fireEvent("mouseout", this, e);
26064 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26067 * @class Roo.bootstrap.menu.Separator
26068 * @extends Roo.bootstrap.Component
26069 * Bootstrap Separator class
26072 * Create a new Separator
26073 * @param {Object} config The config object
26077 Roo.bootstrap.menu.Separator = function(config){
26078 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26081 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26083 getAutoCreate : function(){
26104 * @class Roo.bootstrap.Tooltip
26105 * Bootstrap Tooltip class
26106 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26107 * to determine which dom element triggers the tooltip.
26109 * It needs to add support for additional attributes like tooltip-position
26112 * Create a new Toolti
26113 * @param {Object} config The config object
26116 Roo.bootstrap.Tooltip = function(config){
26117 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26119 this.alignment = Roo.bootstrap.Tooltip.alignment;
26121 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26122 this.alignment = config.alignment;
26127 Roo.apply(Roo.bootstrap.Tooltip, {
26129 * @function init initialize tooltip monitoring.
26133 currentTip : false,
26134 currentRegion : false,
26140 Roo.get(document).on('mouseover', this.enter ,this);
26141 Roo.get(document).on('mouseout', this.leave, this);
26144 this.currentTip = new Roo.bootstrap.Tooltip();
26147 enter : function(ev)
26149 var dom = ev.getTarget();
26151 //Roo.log(['enter',dom]);
26152 var el = Roo.fly(dom);
26153 if (this.currentEl) {
26155 //Roo.log(this.currentEl);
26156 //Roo.log(this.currentEl.contains(dom));
26157 if (this.currentEl == el) {
26160 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26166 if (this.currentTip.el) {
26167 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26171 if(!el || el.dom == document){
26177 // you can not look for children, as if el is the body.. then everythign is the child..
26178 if (!el.attr('tooltip')) { //
26179 if (!el.select("[tooltip]").elements.length) {
26182 // is the mouse over this child...?
26183 bindEl = el.select("[tooltip]").first();
26184 var xy = ev.getXY();
26185 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26186 //Roo.log("not in region.");
26189 //Roo.log("child element over..");
26192 this.currentEl = bindEl;
26193 this.currentTip.bind(bindEl);
26194 this.currentRegion = Roo.lib.Region.getRegion(dom);
26195 this.currentTip.enter();
26198 leave : function(ev)
26200 var dom = ev.getTarget();
26201 //Roo.log(['leave',dom]);
26202 if (!this.currentEl) {
26207 if (dom != this.currentEl.dom) {
26210 var xy = ev.getXY();
26211 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26214 // only activate leave if mouse cursor is outside... bounding box..
26219 if (this.currentTip) {
26220 this.currentTip.leave();
26222 //Roo.log('clear currentEl');
26223 this.currentEl = false;
26228 'left' : ['r-l', [-2,0], 'right'],
26229 'right' : ['l-r', [2,0], 'left'],
26230 'bottom' : ['t-b', [0,2], 'top'],
26231 'top' : [ 'b-t', [0,-2], 'bottom']
26237 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26242 delay : null, // can be { show : 300 , hide: 500}
26246 hoverState : null, //???
26248 placement : 'bottom',
26252 getAutoCreate : function(){
26259 cls : 'tooltip-arrow'
26262 cls : 'tooltip-inner'
26269 bind : function(el)
26275 enter : function () {
26277 if (this.timeout != null) {
26278 clearTimeout(this.timeout);
26281 this.hoverState = 'in';
26282 //Roo.log("enter - show");
26283 if (!this.delay || !this.delay.show) {
26288 this.timeout = setTimeout(function () {
26289 if (_t.hoverState == 'in') {
26292 }, this.delay.show);
26296 clearTimeout(this.timeout);
26298 this.hoverState = 'out';
26299 if (!this.delay || !this.delay.hide) {
26305 this.timeout = setTimeout(function () {
26306 //Roo.log("leave - timeout");
26308 if (_t.hoverState == 'out') {
26310 Roo.bootstrap.Tooltip.currentEl = false;
26315 show : function (msg)
26318 this.render(document.body);
26321 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26323 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26325 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26327 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26329 var placement = typeof this.placement == 'function' ?
26330 this.placement.call(this, this.el, on_el) :
26333 var autoToken = /\s?auto?\s?/i;
26334 var autoPlace = autoToken.test(placement);
26336 placement = placement.replace(autoToken, '') || 'top';
26340 //this.el.setXY([0,0]);
26342 //this.el.dom.style.display='block';
26344 //this.el.appendTo(on_el);
26346 var p = this.getPosition();
26347 var box = this.el.getBox();
26353 var align = this.alignment[placement];
26355 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26357 if(placement == 'top' || placement == 'bottom'){
26359 placement = 'right';
26362 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26363 placement = 'left';
26366 var scroll = Roo.select('body', true).first().getScroll();
26368 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26372 align = this.alignment[placement];
26375 this.el.alignTo(this.bindEl, align[0],align[1]);
26376 //var arrow = this.el.select('.arrow',true).first();
26377 //arrow.set(align[2],
26379 this.el.addClass(placement);
26381 this.el.addClass('in fade');
26383 this.hoverState = null;
26385 if (this.el.hasClass('fade')) {
26396 //this.el.setXY([0,0]);
26397 this.el.removeClass('in');
26413 * @class Roo.bootstrap.LocationPicker
26414 * @extends Roo.bootstrap.Component
26415 * Bootstrap LocationPicker class
26416 * @cfg {Number} latitude Position when init default 0
26417 * @cfg {Number} longitude Position when init default 0
26418 * @cfg {Number} zoom default 15
26419 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26420 * @cfg {Boolean} mapTypeControl default false
26421 * @cfg {Boolean} disableDoubleClickZoom default false
26422 * @cfg {Boolean} scrollwheel default true
26423 * @cfg {Boolean} streetViewControl default false
26424 * @cfg {Number} radius default 0
26425 * @cfg {String} locationName
26426 * @cfg {Boolean} draggable default true
26427 * @cfg {Boolean} enableAutocomplete default false
26428 * @cfg {Boolean} enableReverseGeocode default true
26429 * @cfg {String} markerTitle
26432 * Create a new LocationPicker
26433 * @param {Object} config The config object
26437 Roo.bootstrap.LocationPicker = function(config){
26439 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26444 * Fires when the picker initialized.
26445 * @param {Roo.bootstrap.LocationPicker} this
26446 * @param {Google Location} location
26450 * @event positionchanged
26451 * Fires when the picker position changed.
26452 * @param {Roo.bootstrap.LocationPicker} this
26453 * @param {Google Location} location
26455 positionchanged : true,
26458 * Fires when the map resize.
26459 * @param {Roo.bootstrap.LocationPicker} this
26464 * Fires when the map show.
26465 * @param {Roo.bootstrap.LocationPicker} this
26470 * Fires when the map hide.
26471 * @param {Roo.bootstrap.LocationPicker} this
26476 * Fires when click the map.
26477 * @param {Roo.bootstrap.LocationPicker} this
26478 * @param {Map event} e
26482 * @event mapRightClick
26483 * Fires when right click the map.
26484 * @param {Roo.bootstrap.LocationPicker} this
26485 * @param {Map event} e
26487 mapRightClick : true,
26489 * @event markerClick
26490 * Fires when click the marker.
26491 * @param {Roo.bootstrap.LocationPicker} this
26492 * @param {Map event} e
26494 markerClick : true,
26496 * @event markerRightClick
26497 * Fires when right click the marker.
26498 * @param {Roo.bootstrap.LocationPicker} this
26499 * @param {Map event} e
26501 markerRightClick : true,
26503 * @event OverlayViewDraw
26504 * Fires when OverlayView Draw
26505 * @param {Roo.bootstrap.LocationPicker} this
26507 OverlayViewDraw : true,
26509 * @event OverlayViewOnAdd
26510 * Fires when OverlayView Draw
26511 * @param {Roo.bootstrap.LocationPicker} this
26513 OverlayViewOnAdd : true,
26515 * @event OverlayViewOnRemove
26516 * Fires when OverlayView Draw
26517 * @param {Roo.bootstrap.LocationPicker} this
26519 OverlayViewOnRemove : true,
26521 * @event OverlayViewShow
26522 * Fires when OverlayView Draw
26523 * @param {Roo.bootstrap.LocationPicker} this
26524 * @param {Pixel} cpx
26526 OverlayViewShow : true,
26528 * @event OverlayViewHide
26529 * Fires when OverlayView Draw
26530 * @param {Roo.bootstrap.LocationPicker} this
26532 OverlayViewHide : true,
26534 * @event loadexception
26535 * Fires when load google lib failed.
26536 * @param {Roo.bootstrap.LocationPicker} this
26538 loadexception : true
26543 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26545 gMapContext: false,
26551 mapTypeControl: false,
26552 disableDoubleClickZoom: false,
26554 streetViewControl: false,
26558 enableAutocomplete: false,
26559 enableReverseGeocode: true,
26562 getAutoCreate: function()
26567 cls: 'roo-location-picker'
26573 initEvents: function(ct, position)
26575 if(!this.el.getWidth() || this.isApplied()){
26579 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26584 initial: function()
26586 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26587 this.fireEvent('loadexception', this);
26591 if(!this.mapTypeId){
26592 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26595 this.gMapContext = this.GMapContext();
26597 this.initOverlayView();
26599 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26603 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26604 _this.setPosition(_this.gMapContext.marker.position);
26607 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26608 _this.fireEvent('mapClick', this, event);
26612 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26613 _this.fireEvent('mapRightClick', this, event);
26617 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26618 _this.fireEvent('markerClick', this, event);
26622 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26623 _this.fireEvent('markerRightClick', this, event);
26627 this.setPosition(this.gMapContext.location);
26629 this.fireEvent('initial', this, this.gMapContext.location);
26632 initOverlayView: function()
26636 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26640 _this.fireEvent('OverlayViewDraw', _this);
26645 _this.fireEvent('OverlayViewOnAdd', _this);
26648 onRemove: function()
26650 _this.fireEvent('OverlayViewOnRemove', _this);
26653 show: function(cpx)
26655 _this.fireEvent('OverlayViewShow', _this, cpx);
26660 _this.fireEvent('OverlayViewHide', _this);
26666 fromLatLngToContainerPixel: function(event)
26668 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26671 isApplied: function()
26673 return this.getGmapContext() == false ? false : true;
26676 getGmapContext: function()
26678 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26681 GMapContext: function()
26683 var position = new google.maps.LatLng(this.latitude, this.longitude);
26685 var _map = new google.maps.Map(this.el.dom, {
26688 mapTypeId: this.mapTypeId,
26689 mapTypeControl: this.mapTypeControl,
26690 disableDoubleClickZoom: this.disableDoubleClickZoom,
26691 scrollwheel: this.scrollwheel,
26692 streetViewControl: this.streetViewControl,
26693 locationName: this.locationName,
26694 draggable: this.draggable,
26695 enableAutocomplete: this.enableAutocomplete,
26696 enableReverseGeocode: this.enableReverseGeocode
26699 var _marker = new google.maps.Marker({
26700 position: position,
26702 title: this.markerTitle,
26703 draggable: this.draggable
26710 location: position,
26711 radius: this.radius,
26712 locationName: this.locationName,
26713 addressComponents: {
26714 formatted_address: null,
26715 addressLine1: null,
26716 addressLine2: null,
26718 streetNumber: null,
26722 stateOrProvince: null
26725 domContainer: this.el.dom,
26726 geodecoder: new google.maps.Geocoder()
26730 drawCircle: function(center, radius, options)
26732 if (this.gMapContext.circle != null) {
26733 this.gMapContext.circle.setMap(null);
26737 options = Roo.apply({}, options, {
26738 strokeColor: "#0000FF",
26739 strokeOpacity: .35,
26741 fillColor: "#0000FF",
26745 options.map = this.gMapContext.map;
26746 options.radius = radius;
26747 options.center = center;
26748 this.gMapContext.circle = new google.maps.Circle(options);
26749 return this.gMapContext.circle;
26755 setPosition: function(location)
26757 this.gMapContext.location = location;
26758 this.gMapContext.marker.setPosition(location);
26759 this.gMapContext.map.panTo(location);
26760 this.drawCircle(location, this.gMapContext.radius, {});
26764 if (this.gMapContext.settings.enableReverseGeocode) {
26765 this.gMapContext.geodecoder.geocode({
26766 latLng: this.gMapContext.location
26767 }, function(results, status) {
26769 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26770 _this.gMapContext.locationName = results[0].formatted_address;
26771 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26773 _this.fireEvent('positionchanged', this, location);
26780 this.fireEvent('positionchanged', this, location);
26785 google.maps.event.trigger(this.gMapContext.map, "resize");
26787 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26789 this.fireEvent('resize', this);
26792 setPositionByLatLng: function(latitude, longitude)
26794 this.setPosition(new google.maps.LatLng(latitude, longitude));
26797 getCurrentPosition: function()
26800 latitude: this.gMapContext.location.lat(),
26801 longitude: this.gMapContext.location.lng()
26805 getAddressName: function()
26807 return this.gMapContext.locationName;
26810 getAddressComponents: function()
26812 return this.gMapContext.addressComponents;
26815 address_component_from_google_geocode: function(address_components)
26819 for (var i = 0; i < address_components.length; i++) {
26820 var component = address_components[i];
26821 if (component.types.indexOf("postal_code") >= 0) {
26822 result.postalCode = component.short_name;
26823 } else if (component.types.indexOf("street_number") >= 0) {
26824 result.streetNumber = component.short_name;
26825 } else if (component.types.indexOf("route") >= 0) {
26826 result.streetName = component.short_name;
26827 } else if (component.types.indexOf("neighborhood") >= 0) {
26828 result.city = component.short_name;
26829 } else if (component.types.indexOf("locality") >= 0) {
26830 result.city = component.short_name;
26831 } else if (component.types.indexOf("sublocality") >= 0) {
26832 result.district = component.short_name;
26833 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26834 result.stateOrProvince = component.short_name;
26835 } else if (component.types.indexOf("country") >= 0) {
26836 result.country = component.short_name;
26840 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26841 result.addressLine2 = "";
26845 setZoomLevel: function(zoom)
26847 this.gMapContext.map.setZoom(zoom);
26860 this.fireEvent('show', this);
26871 this.fireEvent('hide', this);
26876 Roo.apply(Roo.bootstrap.LocationPicker, {
26878 OverlayView : function(map, options)
26880 options = options || {};
26894 * @class Roo.bootstrap.Alert
26895 * @extends Roo.bootstrap.Component
26896 * Bootstrap Alert class
26897 * @cfg {String} title The title of alert
26898 * @cfg {String} html The content of alert
26899 * @cfg {String} weight ( success | info | warning | danger )
26900 * @cfg {String} faicon font-awesomeicon
26903 * Create a new alert
26904 * @param {Object} config The config object
26908 Roo.bootstrap.Alert = function(config){
26909 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26913 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26920 getAutoCreate : function()
26929 cls : 'roo-alert-icon'
26934 cls : 'roo-alert-title',
26939 cls : 'roo-alert-text',
26946 cfg.cn[0].cls += ' fa ' + this.faicon;
26950 cfg.cls += ' alert-' + this.weight;
26956 initEvents: function()
26958 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26961 setTitle : function(str)
26963 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26966 setText : function(str)
26968 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26971 setWeight : function(weight)
26974 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26977 this.weight = weight;
26979 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26982 setIcon : function(icon)
26985 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26988 this.faicon = icon;
26990 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27011 * @class Roo.bootstrap.UploadCropbox
27012 * @extends Roo.bootstrap.Component
27013 * Bootstrap UploadCropbox class
27014 * @cfg {String} emptyText show when image has been loaded
27015 * @cfg {String} rotateNotify show when image too small to rotate
27016 * @cfg {Number} errorTimeout default 3000
27017 * @cfg {Number} minWidth default 300
27018 * @cfg {Number} minHeight default 300
27019 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27020 * @cfg {Boolean} isDocument (true|false) default false
27021 * @cfg {String} url action url
27022 * @cfg {String} paramName default 'imageUpload'
27023 * @cfg {String} method default POST
27024 * @cfg {Boolean} loadMask (true|false) default true
27025 * @cfg {Boolean} loadingText default 'Loading...'
27028 * Create a new UploadCropbox
27029 * @param {Object} config The config object
27032 Roo.bootstrap.UploadCropbox = function(config){
27033 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27037 * @event beforeselectfile
27038 * Fire before select file
27039 * @param {Roo.bootstrap.UploadCropbox} this
27041 "beforeselectfile" : true,
27044 * Fire after initEvent
27045 * @param {Roo.bootstrap.UploadCropbox} this
27050 * Fire after initEvent
27051 * @param {Roo.bootstrap.UploadCropbox} this
27052 * @param {String} data
27057 * Fire when preparing the file data
27058 * @param {Roo.bootstrap.UploadCropbox} this
27059 * @param {Object} file
27064 * Fire when get exception
27065 * @param {Roo.bootstrap.UploadCropbox} this
27066 * @param {XMLHttpRequest} xhr
27068 "exception" : true,
27070 * @event beforeloadcanvas
27071 * Fire before load the canvas
27072 * @param {Roo.bootstrap.UploadCropbox} this
27073 * @param {String} src
27075 "beforeloadcanvas" : true,
27078 * Fire when trash image
27079 * @param {Roo.bootstrap.UploadCropbox} this
27084 * Fire when download the image
27085 * @param {Roo.bootstrap.UploadCropbox} this
27089 * @event footerbuttonclick
27090 * Fire when footerbuttonclick
27091 * @param {Roo.bootstrap.UploadCropbox} this
27092 * @param {String} type
27094 "footerbuttonclick" : true,
27098 * @param {Roo.bootstrap.UploadCropbox} this
27103 * Fire when rotate the image
27104 * @param {Roo.bootstrap.UploadCropbox} this
27105 * @param {String} pos
27110 * Fire when inspect the file
27111 * @param {Roo.bootstrap.UploadCropbox} this
27112 * @param {Object} file
27117 * Fire when xhr upload the file
27118 * @param {Roo.bootstrap.UploadCropbox} this
27119 * @param {Object} data
27124 * Fire when arrange the file data
27125 * @param {Roo.bootstrap.UploadCropbox} this
27126 * @param {Object} formData
27131 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27134 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27136 emptyText : 'Click to upload image',
27137 rotateNotify : 'Image is too small to rotate',
27138 errorTimeout : 3000,
27152 cropType : 'image/jpeg',
27154 canvasLoaded : false,
27155 isDocument : false,
27157 paramName : 'imageUpload',
27159 loadingText : 'Loading...',
27162 getAutoCreate : function()
27166 cls : 'roo-upload-cropbox',
27170 cls : 'roo-upload-cropbox-selector',
27175 cls : 'roo-upload-cropbox-body',
27176 style : 'cursor:pointer',
27180 cls : 'roo-upload-cropbox-preview'
27184 cls : 'roo-upload-cropbox-thumb'
27188 cls : 'roo-upload-cropbox-empty-notify',
27189 html : this.emptyText
27193 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27194 html : this.rotateNotify
27200 cls : 'roo-upload-cropbox-footer',
27203 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27213 onRender : function(ct, position)
27215 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27217 if (this.buttons.length) {
27219 Roo.each(this.buttons, function(bb) {
27221 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27223 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27229 this.maskEl = this.el;
27233 initEvents : function()
27235 this.urlAPI = (window.createObjectURL && window) ||
27236 (window.URL && URL.revokeObjectURL && URL) ||
27237 (window.webkitURL && webkitURL);
27239 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27240 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27242 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27243 this.selectorEl.hide();
27245 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27246 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27248 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27249 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27250 this.thumbEl.hide();
27252 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27253 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27255 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27256 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27257 this.errorEl.hide();
27259 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27260 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27261 this.footerEl.hide();
27263 this.setThumbBoxSize();
27269 this.fireEvent('initial', this);
27276 window.addEventListener("resize", function() { _this.resize(); } );
27278 this.bodyEl.on('click', this.beforeSelectFile, this);
27281 this.bodyEl.on('touchstart', this.onTouchStart, this);
27282 this.bodyEl.on('touchmove', this.onTouchMove, this);
27283 this.bodyEl.on('touchend', this.onTouchEnd, this);
27287 this.bodyEl.on('mousedown', this.onMouseDown, this);
27288 this.bodyEl.on('mousemove', this.onMouseMove, this);
27289 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27290 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27291 Roo.get(document).on('mouseup', this.onMouseUp, this);
27294 this.selectorEl.on('change', this.onFileSelected, this);
27300 this.baseScale = 1;
27302 this.baseRotate = 1;
27303 this.dragable = false;
27304 this.pinching = false;
27307 this.cropData = false;
27308 this.notifyEl.dom.innerHTML = this.emptyText;
27310 this.selectorEl.dom.value = '';
27314 resize : function()
27316 if(this.fireEvent('resize', this) != false){
27317 this.setThumbBoxPosition();
27318 this.setCanvasPosition();
27322 onFooterButtonClick : function(e, el, o, type)
27325 case 'rotate-left' :
27326 this.onRotateLeft(e);
27328 case 'rotate-right' :
27329 this.onRotateRight(e);
27332 this.beforeSelectFile(e);
27347 this.fireEvent('footerbuttonclick', this, type);
27350 beforeSelectFile : function(e)
27352 e.preventDefault();
27354 if(this.fireEvent('beforeselectfile', this) != false){
27355 this.selectorEl.dom.click();
27359 onFileSelected : function(e)
27361 e.preventDefault();
27363 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27367 var file = this.selectorEl.dom.files[0];
27369 if(this.fireEvent('inspect', this, file) != false){
27370 this.prepare(file);
27375 trash : function(e)
27377 this.fireEvent('trash', this);
27380 download : function(e)
27382 this.fireEvent('download', this);
27385 loadCanvas : function(src)
27387 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27391 this.imageEl = document.createElement('img');
27395 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27397 this.imageEl.src = src;
27401 onLoadCanvas : function()
27403 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27404 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27406 this.bodyEl.un('click', this.beforeSelectFile, this);
27408 this.notifyEl.hide();
27409 this.thumbEl.show();
27410 this.footerEl.show();
27412 this.baseRotateLevel();
27414 if(this.isDocument){
27415 this.setThumbBoxSize();
27418 this.setThumbBoxPosition();
27420 this.baseScaleLevel();
27426 this.canvasLoaded = true;
27429 this.maskEl.unmask();
27434 setCanvasPosition : function()
27436 if(!this.canvasEl){
27440 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27441 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27443 this.previewEl.setLeft(pw);
27444 this.previewEl.setTop(ph);
27448 onMouseDown : function(e)
27452 this.dragable = true;
27453 this.pinching = false;
27455 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27456 this.dragable = false;
27460 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27461 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27465 onMouseMove : function(e)
27469 if(!this.canvasLoaded){
27473 if (!this.dragable){
27477 var minX = Math.ceil(this.thumbEl.getLeft(true));
27478 var minY = Math.ceil(this.thumbEl.getTop(true));
27480 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27481 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27483 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27484 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27486 x = x - this.mouseX;
27487 y = y - this.mouseY;
27489 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27490 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27492 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27493 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27495 this.previewEl.setLeft(bgX);
27496 this.previewEl.setTop(bgY);
27498 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27499 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27502 onMouseUp : function(e)
27506 this.dragable = false;
27509 onMouseWheel : function(e)
27513 this.startScale = this.scale;
27515 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27517 if(!this.zoomable()){
27518 this.scale = this.startScale;
27527 zoomable : function()
27529 var minScale = this.thumbEl.getWidth() / this.minWidth;
27531 if(this.minWidth < this.minHeight){
27532 minScale = this.thumbEl.getHeight() / this.minHeight;
27535 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27536 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27540 (this.rotate == 0 || this.rotate == 180) &&
27542 width > this.imageEl.OriginWidth ||
27543 height > this.imageEl.OriginHeight ||
27544 (width < this.minWidth && height < this.minHeight)
27552 (this.rotate == 90 || this.rotate == 270) &&
27554 width > this.imageEl.OriginWidth ||
27555 height > this.imageEl.OriginHeight ||
27556 (width < this.minHeight && height < this.minWidth)
27563 !this.isDocument &&
27564 (this.rotate == 0 || this.rotate == 180) &&
27566 width < this.minWidth ||
27567 width > this.imageEl.OriginWidth ||
27568 height < this.minHeight ||
27569 height > this.imageEl.OriginHeight
27576 !this.isDocument &&
27577 (this.rotate == 90 || this.rotate == 270) &&
27579 width < this.minHeight ||
27580 width > this.imageEl.OriginWidth ||
27581 height < this.minWidth ||
27582 height > this.imageEl.OriginHeight
27592 onRotateLeft : function(e)
27594 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27596 var minScale = this.thumbEl.getWidth() / this.minWidth;
27598 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27599 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27601 this.startScale = this.scale;
27603 while (this.getScaleLevel() < minScale){
27605 this.scale = this.scale + 1;
27607 if(!this.zoomable()){
27612 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27613 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27618 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27625 this.scale = this.startScale;
27627 this.onRotateFail();
27632 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27634 if(this.isDocument){
27635 this.setThumbBoxSize();
27636 this.setThumbBoxPosition();
27637 this.setCanvasPosition();
27642 this.fireEvent('rotate', this, 'left');
27646 onRotateRight : function(e)
27648 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27650 var minScale = this.thumbEl.getWidth() / this.minWidth;
27652 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27653 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27655 this.startScale = this.scale;
27657 while (this.getScaleLevel() < minScale){
27659 this.scale = this.scale + 1;
27661 if(!this.zoomable()){
27666 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27667 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27672 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27679 this.scale = this.startScale;
27681 this.onRotateFail();
27686 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27688 if(this.isDocument){
27689 this.setThumbBoxSize();
27690 this.setThumbBoxPosition();
27691 this.setCanvasPosition();
27696 this.fireEvent('rotate', this, 'right');
27699 onRotateFail : function()
27701 this.errorEl.show(true);
27705 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27710 this.previewEl.dom.innerHTML = '';
27712 var canvasEl = document.createElement("canvas");
27714 var contextEl = canvasEl.getContext("2d");
27716 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27717 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27718 var center = this.imageEl.OriginWidth / 2;
27720 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27721 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27722 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27723 center = this.imageEl.OriginHeight / 2;
27726 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27728 contextEl.translate(center, center);
27729 contextEl.rotate(this.rotate * Math.PI / 180);
27731 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27733 this.canvasEl = document.createElement("canvas");
27735 this.contextEl = this.canvasEl.getContext("2d");
27737 switch (this.rotate) {
27740 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27741 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27743 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27748 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27749 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27751 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27752 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);
27756 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27761 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27762 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27764 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27765 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);
27769 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);
27774 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27775 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27777 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27778 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27782 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);
27789 this.previewEl.appendChild(this.canvasEl);
27791 this.setCanvasPosition();
27796 if(!this.canvasLoaded){
27800 var imageCanvas = document.createElement("canvas");
27802 var imageContext = imageCanvas.getContext("2d");
27804 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27805 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27807 var center = imageCanvas.width / 2;
27809 imageContext.translate(center, center);
27811 imageContext.rotate(this.rotate * Math.PI / 180);
27813 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27815 var canvas = document.createElement("canvas");
27817 var context = canvas.getContext("2d");
27819 canvas.width = this.minWidth;
27820 canvas.height = this.minHeight;
27822 switch (this.rotate) {
27825 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27826 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27828 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27829 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27831 var targetWidth = this.minWidth - 2 * x;
27832 var targetHeight = this.minHeight - 2 * y;
27836 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27837 scale = targetWidth / width;
27840 if(x > 0 && y == 0){
27841 scale = targetHeight / height;
27844 if(x > 0 && y > 0){
27845 scale = targetWidth / width;
27847 if(width < height){
27848 scale = targetHeight / height;
27852 context.scale(scale, scale);
27854 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27855 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27857 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27858 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27860 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27865 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27866 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27868 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27869 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27871 var targetWidth = this.minWidth - 2 * x;
27872 var targetHeight = this.minHeight - 2 * y;
27876 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27877 scale = targetWidth / width;
27880 if(x > 0 && y == 0){
27881 scale = targetHeight / height;
27884 if(x > 0 && y > 0){
27885 scale = targetWidth / width;
27887 if(width < height){
27888 scale = targetHeight / height;
27892 context.scale(scale, scale);
27894 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27895 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27897 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27898 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27900 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27902 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27907 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27908 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27910 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27911 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27913 var targetWidth = this.minWidth - 2 * x;
27914 var targetHeight = this.minHeight - 2 * y;
27918 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27919 scale = targetWidth / width;
27922 if(x > 0 && y == 0){
27923 scale = targetHeight / height;
27926 if(x > 0 && y > 0){
27927 scale = targetWidth / width;
27929 if(width < height){
27930 scale = targetHeight / height;
27934 context.scale(scale, scale);
27936 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27937 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27939 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27940 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27942 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27943 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27945 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27950 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27951 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27953 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27954 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27956 var targetWidth = this.minWidth - 2 * x;
27957 var targetHeight = this.minHeight - 2 * y;
27961 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27962 scale = targetWidth / width;
27965 if(x > 0 && y == 0){
27966 scale = targetHeight / height;
27969 if(x > 0 && y > 0){
27970 scale = targetWidth / width;
27972 if(width < height){
27973 scale = targetHeight / height;
27977 context.scale(scale, scale);
27979 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27980 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27982 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27983 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27985 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27987 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27994 this.cropData = canvas.toDataURL(this.cropType);
27996 if(this.fireEvent('crop', this, this.cropData) !== false){
27997 this.process(this.file, this.cropData);
28004 setThumbBoxSize : function()
28008 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28009 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28010 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28012 this.minWidth = width;
28013 this.minHeight = height;
28015 if(this.rotate == 90 || this.rotate == 270){
28016 this.minWidth = height;
28017 this.minHeight = width;
28022 width = Math.ceil(this.minWidth * height / this.minHeight);
28024 if(this.minWidth > this.minHeight){
28026 height = Math.ceil(this.minHeight * width / this.minWidth);
28029 this.thumbEl.setStyle({
28030 width : width + 'px',
28031 height : height + 'px'
28038 setThumbBoxPosition : function()
28040 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28041 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28043 this.thumbEl.setLeft(x);
28044 this.thumbEl.setTop(y);
28048 baseRotateLevel : function()
28050 this.baseRotate = 1;
28053 typeof(this.exif) != 'undefined' &&
28054 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28055 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28057 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28060 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28064 baseScaleLevel : function()
28068 if(this.isDocument){
28070 if(this.baseRotate == 6 || this.baseRotate == 8){
28072 height = this.thumbEl.getHeight();
28073 this.baseScale = height / this.imageEl.OriginWidth;
28075 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28076 width = this.thumbEl.getWidth();
28077 this.baseScale = width / this.imageEl.OriginHeight;
28083 height = this.thumbEl.getHeight();
28084 this.baseScale = height / this.imageEl.OriginHeight;
28086 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28087 width = this.thumbEl.getWidth();
28088 this.baseScale = width / this.imageEl.OriginWidth;
28094 if(this.baseRotate == 6 || this.baseRotate == 8){
28096 width = this.thumbEl.getHeight();
28097 this.baseScale = width / this.imageEl.OriginHeight;
28099 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28100 height = this.thumbEl.getWidth();
28101 this.baseScale = height / this.imageEl.OriginHeight;
28104 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28105 height = this.thumbEl.getWidth();
28106 this.baseScale = height / this.imageEl.OriginHeight;
28108 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28109 width = this.thumbEl.getHeight();
28110 this.baseScale = width / this.imageEl.OriginWidth;
28117 width = this.thumbEl.getWidth();
28118 this.baseScale = width / this.imageEl.OriginWidth;
28120 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28121 height = this.thumbEl.getHeight();
28122 this.baseScale = height / this.imageEl.OriginHeight;
28125 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28127 height = this.thumbEl.getHeight();
28128 this.baseScale = height / this.imageEl.OriginHeight;
28130 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28131 width = this.thumbEl.getWidth();
28132 this.baseScale = width / this.imageEl.OriginWidth;
28140 getScaleLevel : function()
28142 return this.baseScale * Math.pow(1.1, this.scale);
28145 onTouchStart : function(e)
28147 if(!this.canvasLoaded){
28148 this.beforeSelectFile(e);
28152 var touches = e.browserEvent.touches;
28158 if(touches.length == 1){
28159 this.onMouseDown(e);
28163 if(touches.length != 2){
28169 for(var i = 0, finger; finger = touches[i]; i++){
28170 coords.push(finger.pageX, finger.pageY);
28173 var x = Math.pow(coords[0] - coords[2], 2);
28174 var y = Math.pow(coords[1] - coords[3], 2);
28176 this.startDistance = Math.sqrt(x + y);
28178 this.startScale = this.scale;
28180 this.pinching = true;
28181 this.dragable = false;
28185 onTouchMove : function(e)
28187 if(!this.pinching && !this.dragable){
28191 var touches = e.browserEvent.touches;
28198 this.onMouseMove(e);
28204 for(var i = 0, finger; finger = touches[i]; i++){
28205 coords.push(finger.pageX, finger.pageY);
28208 var x = Math.pow(coords[0] - coords[2], 2);
28209 var y = Math.pow(coords[1] - coords[3], 2);
28211 this.endDistance = Math.sqrt(x + y);
28213 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28215 if(!this.zoomable()){
28216 this.scale = this.startScale;
28224 onTouchEnd : function(e)
28226 this.pinching = false;
28227 this.dragable = false;
28231 process : function(file, crop)
28234 this.maskEl.mask(this.loadingText);
28237 this.xhr = new XMLHttpRequest();
28239 file.xhr = this.xhr;
28241 this.xhr.open(this.method, this.url, true);
28244 "Accept": "application/json",
28245 "Cache-Control": "no-cache",
28246 "X-Requested-With": "XMLHttpRequest"
28249 for (var headerName in headers) {
28250 var headerValue = headers[headerName];
28252 this.xhr.setRequestHeader(headerName, headerValue);
28258 this.xhr.onload = function()
28260 _this.xhrOnLoad(_this.xhr);
28263 this.xhr.onerror = function()
28265 _this.xhrOnError(_this.xhr);
28268 var formData = new FormData();
28270 formData.append('returnHTML', 'NO');
28273 formData.append('crop', crop);
28276 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28277 formData.append(this.paramName, file, file.name);
28280 if(typeof(file.filename) != 'undefined'){
28281 formData.append('filename', file.filename);
28284 if(typeof(file.mimetype) != 'undefined'){
28285 formData.append('mimetype', file.mimetype);
28288 if(this.fireEvent('arrange', this, formData) != false){
28289 this.xhr.send(formData);
28293 xhrOnLoad : function(xhr)
28296 this.maskEl.unmask();
28299 if (xhr.readyState !== 4) {
28300 this.fireEvent('exception', this, xhr);
28304 var response = Roo.decode(xhr.responseText);
28306 if(!response.success){
28307 this.fireEvent('exception', this, xhr);
28311 var response = Roo.decode(xhr.responseText);
28313 this.fireEvent('upload', this, response);
28317 xhrOnError : function()
28320 this.maskEl.unmask();
28323 Roo.log('xhr on error');
28325 var response = Roo.decode(xhr.responseText);
28331 prepare : function(file)
28334 this.maskEl.mask(this.loadingText);
28340 if(typeof(file) === 'string'){
28341 this.loadCanvas(file);
28345 if(!file || !this.urlAPI){
28350 this.cropType = file.type;
28354 if(this.fireEvent('prepare', this, this.file) != false){
28356 var reader = new FileReader();
28358 reader.onload = function (e) {
28359 if (e.target.error) {
28360 Roo.log(e.target.error);
28364 var buffer = e.target.result,
28365 dataView = new DataView(buffer),
28367 maxOffset = dataView.byteLength - 4,
28371 if (dataView.getUint16(0) === 0xffd8) {
28372 while (offset < maxOffset) {
28373 markerBytes = dataView.getUint16(offset);
28375 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28376 markerLength = dataView.getUint16(offset + 2) + 2;
28377 if (offset + markerLength > dataView.byteLength) {
28378 Roo.log('Invalid meta data: Invalid segment size.');
28382 if(markerBytes == 0xffe1){
28383 _this.parseExifData(
28390 offset += markerLength;
28400 var url = _this.urlAPI.createObjectURL(_this.file);
28402 _this.loadCanvas(url);
28407 reader.readAsArrayBuffer(this.file);
28413 parseExifData : function(dataView, offset, length)
28415 var tiffOffset = offset + 10,
28419 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28420 // No Exif data, might be XMP data instead
28424 // Check for the ASCII code for "Exif" (0x45786966):
28425 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28426 // No Exif data, might be XMP data instead
28429 if (tiffOffset + 8 > dataView.byteLength) {
28430 Roo.log('Invalid Exif data: Invalid segment size.');
28433 // Check for the two null bytes:
28434 if (dataView.getUint16(offset + 8) !== 0x0000) {
28435 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28438 // Check the byte alignment:
28439 switch (dataView.getUint16(tiffOffset)) {
28441 littleEndian = true;
28444 littleEndian = false;
28447 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28450 // Check for the TIFF tag marker (0x002A):
28451 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28452 Roo.log('Invalid Exif data: Missing TIFF marker.');
28455 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28456 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28458 this.parseExifTags(
28461 tiffOffset + dirOffset,
28466 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28471 if (dirOffset + 6 > dataView.byteLength) {
28472 Roo.log('Invalid Exif data: Invalid directory offset.');
28475 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28476 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28477 if (dirEndOffset + 4 > dataView.byteLength) {
28478 Roo.log('Invalid Exif data: Invalid directory size.');
28481 for (i = 0; i < tagsNumber; i += 1) {
28485 dirOffset + 2 + 12 * i, // tag offset
28489 // Return the offset to the next directory:
28490 return dataView.getUint32(dirEndOffset, littleEndian);
28493 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28495 var tag = dataView.getUint16(offset, littleEndian);
28497 this.exif[tag] = this.getExifValue(
28501 dataView.getUint16(offset + 2, littleEndian), // tag type
28502 dataView.getUint32(offset + 4, littleEndian), // tag length
28507 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28509 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28518 Roo.log('Invalid Exif data: Invalid tag type.');
28522 tagSize = tagType.size * length;
28523 // Determine if the value is contained in the dataOffset bytes,
28524 // or if the value at the dataOffset is a pointer to the actual data:
28525 dataOffset = tagSize > 4 ?
28526 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28527 if (dataOffset + tagSize > dataView.byteLength) {
28528 Roo.log('Invalid Exif data: Invalid data offset.');
28531 if (length === 1) {
28532 return tagType.getValue(dataView, dataOffset, littleEndian);
28535 for (i = 0; i < length; i += 1) {
28536 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28539 if (tagType.ascii) {
28541 // Concatenate the chars:
28542 for (i = 0; i < values.length; i += 1) {
28544 // Ignore the terminating NULL byte(s):
28545 if (c === '\u0000') {
28557 Roo.apply(Roo.bootstrap.UploadCropbox, {
28559 'Orientation': 0x0112
28563 1: 0, //'top-left',
28565 3: 180, //'bottom-right',
28566 // 4: 'bottom-left',
28568 6: 90, //'right-top',
28569 // 7: 'right-bottom',
28570 8: 270 //'left-bottom'
28574 // byte, 8-bit unsigned int:
28576 getValue: function (dataView, dataOffset) {
28577 return dataView.getUint8(dataOffset);
28581 // ascii, 8-bit byte:
28583 getValue: function (dataView, dataOffset) {
28584 return String.fromCharCode(dataView.getUint8(dataOffset));
28589 // short, 16 bit int:
28591 getValue: function (dataView, dataOffset, littleEndian) {
28592 return dataView.getUint16(dataOffset, littleEndian);
28596 // long, 32 bit int:
28598 getValue: function (dataView, dataOffset, littleEndian) {
28599 return dataView.getUint32(dataOffset, littleEndian);
28603 // rational = two long values, first is numerator, second is denominator:
28605 getValue: function (dataView, dataOffset, littleEndian) {
28606 return dataView.getUint32(dataOffset, littleEndian) /
28607 dataView.getUint32(dataOffset + 4, littleEndian);
28611 // slong, 32 bit signed int:
28613 getValue: function (dataView, dataOffset, littleEndian) {
28614 return dataView.getInt32(dataOffset, littleEndian);
28618 // srational, two slongs, first is numerator, second is denominator:
28620 getValue: function (dataView, dataOffset, littleEndian) {
28621 return dataView.getInt32(dataOffset, littleEndian) /
28622 dataView.getInt32(dataOffset + 4, littleEndian);
28632 cls : 'btn-group roo-upload-cropbox-rotate-left',
28633 action : 'rotate-left',
28637 cls : 'btn btn-default',
28638 html : '<i class="fa fa-undo"></i>'
28644 cls : 'btn-group roo-upload-cropbox-picture',
28645 action : 'picture',
28649 cls : 'btn btn-default',
28650 html : '<i class="fa fa-picture-o"></i>'
28656 cls : 'btn-group roo-upload-cropbox-rotate-right',
28657 action : 'rotate-right',
28661 cls : 'btn btn-default',
28662 html : '<i class="fa fa-repeat"></i>'
28670 cls : 'btn-group roo-upload-cropbox-rotate-left',
28671 action : 'rotate-left',
28675 cls : 'btn btn-default',
28676 html : '<i class="fa fa-undo"></i>'
28682 cls : 'btn-group roo-upload-cropbox-download',
28683 action : 'download',
28687 cls : 'btn btn-default',
28688 html : '<i class="fa fa-download"></i>'
28694 cls : 'btn-group roo-upload-cropbox-crop',
28699 cls : 'btn btn-default',
28700 html : '<i class="fa fa-crop"></i>'
28706 cls : 'btn-group roo-upload-cropbox-trash',
28711 cls : 'btn btn-default',
28712 html : '<i class="fa fa-trash"></i>'
28718 cls : 'btn-group roo-upload-cropbox-rotate-right',
28719 action : 'rotate-right',
28723 cls : 'btn btn-default',
28724 html : '<i class="fa fa-repeat"></i>'
28732 cls : 'btn-group roo-upload-cropbox-rotate-left',
28733 action : 'rotate-left',
28737 cls : 'btn btn-default',
28738 html : '<i class="fa fa-undo"></i>'
28744 cls : 'btn-group roo-upload-cropbox-rotate-right',
28745 action : 'rotate-right',
28749 cls : 'btn btn-default',
28750 html : '<i class="fa fa-repeat"></i>'
28763 * @class Roo.bootstrap.DocumentManager
28764 * @extends Roo.bootstrap.Component
28765 * Bootstrap DocumentManager class
28766 * @cfg {String} paramName default 'imageUpload'
28767 * @cfg {String} toolTipName default 'filename'
28768 * @cfg {String} method default POST
28769 * @cfg {String} url action url
28770 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28771 * @cfg {Boolean} multiple multiple upload default true
28772 * @cfg {Number} thumbSize default 300
28773 * @cfg {String} fieldLabel
28774 * @cfg {Number} labelWidth default 4
28775 * @cfg {String} labelAlign (left|top) default left
28776 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28777 * @cfg {Number} labellg set the width of label (1-12)
28778 * @cfg {Number} labelmd set the width of label (1-12)
28779 * @cfg {Number} labelsm set the width of label (1-12)
28780 * @cfg {Number} labelxs set the width of label (1-12)
28783 * Create a new DocumentManager
28784 * @param {Object} config The config object
28787 Roo.bootstrap.DocumentManager = function(config){
28788 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28791 this.delegates = [];
28796 * Fire when initial the DocumentManager
28797 * @param {Roo.bootstrap.DocumentManager} this
28802 * inspect selected file
28803 * @param {Roo.bootstrap.DocumentManager} this
28804 * @param {File} file
28809 * Fire when xhr load exception
28810 * @param {Roo.bootstrap.DocumentManager} this
28811 * @param {XMLHttpRequest} xhr
28813 "exception" : true,
28815 * @event afterupload
28816 * Fire when xhr load exception
28817 * @param {Roo.bootstrap.DocumentManager} this
28818 * @param {XMLHttpRequest} xhr
28820 "afterupload" : true,
28823 * prepare the form data
28824 * @param {Roo.bootstrap.DocumentManager} this
28825 * @param {Object} formData
28830 * Fire when remove the file
28831 * @param {Roo.bootstrap.DocumentManager} this
28832 * @param {Object} file
28837 * Fire after refresh the file
28838 * @param {Roo.bootstrap.DocumentManager} this
28843 * Fire after click the image
28844 * @param {Roo.bootstrap.DocumentManager} this
28845 * @param {Object} file
28850 * Fire when upload a image and editable set to true
28851 * @param {Roo.bootstrap.DocumentManager} this
28852 * @param {Object} file
28856 * @event beforeselectfile
28857 * Fire before select file
28858 * @param {Roo.bootstrap.DocumentManager} this
28860 "beforeselectfile" : true,
28863 * Fire before process file
28864 * @param {Roo.bootstrap.DocumentManager} this
28865 * @param {Object} file
28869 * @event previewrendered
28870 * Fire when preview rendered
28871 * @param {Roo.bootstrap.DocumentManager} this
28872 * @param {Object} file
28874 "previewrendered" : true,
28877 "previewResize" : true
28882 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28891 paramName : 'imageUpload',
28892 toolTipName : 'filename',
28895 labelAlign : 'left',
28905 getAutoCreate : function()
28907 var managerWidget = {
28909 cls : 'roo-document-manager',
28913 cls : 'roo-document-manager-selector',
28918 cls : 'roo-document-manager-uploader',
28922 cls : 'roo-document-manager-upload-btn',
28923 html : '<i class="fa fa-plus"></i>'
28934 cls : 'column col-md-12',
28939 if(this.fieldLabel.length){
28944 cls : 'column col-md-12',
28945 html : this.fieldLabel
28949 cls : 'column col-md-12',
28954 if(this.labelAlign == 'left'){
28959 html : this.fieldLabel
28968 if(this.labelWidth > 12){
28969 content[0].style = "width: " + this.labelWidth + 'px';
28972 if(this.labelWidth < 13 && this.labelmd == 0){
28973 this.labelmd = this.labelWidth;
28976 if(this.labellg > 0){
28977 content[0].cls += ' col-lg-' + this.labellg;
28978 content[1].cls += ' col-lg-' + (12 - this.labellg);
28981 if(this.labelmd > 0){
28982 content[0].cls += ' col-md-' + this.labelmd;
28983 content[1].cls += ' col-md-' + (12 - this.labelmd);
28986 if(this.labelsm > 0){
28987 content[0].cls += ' col-sm-' + this.labelsm;
28988 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28991 if(this.labelxs > 0){
28992 content[0].cls += ' col-xs-' + this.labelxs;
28993 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29001 cls : 'row clearfix',
29009 initEvents : function()
29011 this.managerEl = this.el.select('.roo-document-manager', true).first();
29012 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29014 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29015 this.selectorEl.hide();
29018 this.selectorEl.attr('multiple', 'multiple');
29021 this.selectorEl.on('change', this.onFileSelected, this);
29023 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29024 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29026 this.uploader.on('click', this.onUploaderClick, this);
29028 this.renderProgressDialog();
29032 window.addEventListener("resize", function() { _this.refresh(); } );
29034 this.fireEvent('initial', this);
29037 renderProgressDialog : function()
29041 this.progressDialog = new Roo.bootstrap.Modal({
29042 cls : 'roo-document-manager-progress-dialog',
29043 allow_close : false,
29053 btnclick : function() {
29054 _this.uploadCancel();
29060 this.progressDialog.render(Roo.get(document.body));
29062 this.progress = new Roo.bootstrap.Progress({
29063 cls : 'roo-document-manager-progress',
29068 this.progress.render(this.progressDialog.getChildContainer());
29070 this.progressBar = new Roo.bootstrap.ProgressBar({
29071 cls : 'roo-document-manager-progress-bar',
29074 aria_valuemax : 12,
29078 this.progressBar.render(this.progress.getChildContainer());
29081 onUploaderClick : function(e)
29083 e.preventDefault();
29085 if(this.fireEvent('beforeselectfile', this) != false){
29086 this.selectorEl.dom.click();
29091 onFileSelected : function(e)
29093 e.preventDefault();
29095 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29099 Roo.each(this.selectorEl.dom.files, function(file){
29100 if(this.fireEvent('inspect', this, file) != false){
29101 this.files.push(file);
29111 this.selectorEl.dom.value = '';
29113 if(!this.files || !this.files.length){
29117 if(this.boxes > 0 && this.files.length > this.boxes){
29118 this.files = this.files.slice(0, this.boxes);
29121 this.uploader.show();
29123 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29124 this.uploader.hide();
29133 Roo.each(this.files, function(file){
29135 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29136 var f = this.renderPreview(file);
29141 if(file.type.indexOf('image') != -1){
29142 this.delegates.push(
29144 _this.process(file);
29145 }).createDelegate(this)
29153 _this.process(file);
29154 }).createDelegate(this)
29159 this.files = files;
29161 this.delegates = this.delegates.concat(docs);
29163 if(!this.delegates.length){
29168 this.progressBar.aria_valuemax = this.delegates.length;
29175 arrange : function()
29177 if(!this.delegates.length){
29178 this.progressDialog.hide();
29183 var delegate = this.delegates.shift();
29185 this.progressDialog.show();
29187 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29189 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29194 refresh : function()
29196 this.uploader.show();
29198 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29199 this.uploader.hide();
29202 Roo.isTouch ? this.closable(false) : this.closable(true);
29204 this.fireEvent('refresh', this);
29207 onRemove : function(e, el, o)
29209 e.preventDefault();
29211 this.fireEvent('remove', this, o);
29215 remove : function(o)
29219 Roo.each(this.files, function(file){
29220 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29229 this.files = files;
29236 Roo.each(this.files, function(file){
29241 file.target.remove();
29250 onClick : function(e, el, o)
29252 e.preventDefault();
29254 this.fireEvent('click', this, o);
29258 closable : function(closable)
29260 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29262 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29274 xhrOnLoad : function(xhr)
29276 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29280 if (xhr.readyState !== 4) {
29282 this.fireEvent('exception', this, xhr);
29286 var response = Roo.decode(xhr.responseText);
29288 if(!response.success){
29290 this.fireEvent('exception', this, xhr);
29294 var file = this.renderPreview(response.data);
29296 this.files.push(file);
29300 this.fireEvent('afterupload', this, xhr);
29304 xhrOnError : function(xhr)
29306 Roo.log('xhr on error');
29308 var response = Roo.decode(xhr.responseText);
29315 process : function(file)
29317 if(this.fireEvent('process', this, file) !== false){
29318 if(this.editable && file.type.indexOf('image') != -1){
29319 this.fireEvent('edit', this, file);
29323 this.uploadStart(file, false);
29330 uploadStart : function(file, crop)
29332 this.xhr = new XMLHttpRequest();
29334 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29339 file.xhr = this.xhr;
29341 this.managerEl.createChild({
29343 cls : 'roo-document-manager-loading',
29347 tooltip : file.name,
29348 cls : 'roo-document-manager-thumb',
29349 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29355 this.xhr.open(this.method, this.url, true);
29358 "Accept": "application/json",
29359 "Cache-Control": "no-cache",
29360 "X-Requested-With": "XMLHttpRequest"
29363 for (var headerName in headers) {
29364 var headerValue = headers[headerName];
29366 this.xhr.setRequestHeader(headerName, headerValue);
29372 this.xhr.onload = function()
29374 _this.xhrOnLoad(_this.xhr);
29377 this.xhr.onerror = function()
29379 _this.xhrOnError(_this.xhr);
29382 var formData = new FormData();
29384 formData.append('returnHTML', 'NO');
29387 formData.append('crop', crop);
29390 formData.append(this.paramName, file, file.name);
29397 if(this.fireEvent('prepare', this, formData, options) != false){
29399 if(options.manually){
29403 this.xhr.send(formData);
29407 this.uploadCancel();
29410 uploadCancel : function()
29416 this.delegates = [];
29418 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29425 renderPreview : function(file)
29427 if(typeof(file.target) != 'undefined' && file.target){
29431 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29433 var previewEl = this.managerEl.createChild({
29435 cls : 'roo-document-manager-preview',
29439 tooltip : file[this.toolTipName],
29440 cls : 'roo-document-manager-thumb',
29441 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29446 html : '<i class="fa fa-times-circle"></i>'
29451 var close = previewEl.select('button.close', true).first();
29453 close.on('click', this.onRemove, this, file);
29455 file.target = previewEl;
29457 var image = previewEl.select('img', true).first();
29461 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29463 image.on('click', this.onClick, this, file);
29465 this.fireEvent('previewrendered', this, file);
29471 onPreviewLoad : function(file, image)
29473 if(typeof(file.target) == 'undefined' || !file.target){
29477 var width = image.dom.naturalWidth || image.dom.width;
29478 var height = image.dom.naturalHeight || image.dom.height;
29480 if(!this.previewResize) {
29484 if(width > height){
29485 file.target.addClass('wide');
29489 file.target.addClass('tall');
29494 uploadFromSource : function(file, crop)
29496 this.xhr = new XMLHttpRequest();
29498 this.managerEl.createChild({
29500 cls : 'roo-document-manager-loading',
29504 tooltip : file.name,
29505 cls : 'roo-document-manager-thumb',
29506 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29512 this.xhr.open(this.method, this.url, true);
29515 "Accept": "application/json",
29516 "Cache-Control": "no-cache",
29517 "X-Requested-With": "XMLHttpRequest"
29520 for (var headerName in headers) {
29521 var headerValue = headers[headerName];
29523 this.xhr.setRequestHeader(headerName, headerValue);
29529 this.xhr.onload = function()
29531 _this.xhrOnLoad(_this.xhr);
29534 this.xhr.onerror = function()
29536 _this.xhrOnError(_this.xhr);
29539 var formData = new FormData();
29541 formData.append('returnHTML', 'NO');
29543 formData.append('crop', crop);
29545 if(typeof(file.filename) != 'undefined'){
29546 formData.append('filename', file.filename);
29549 if(typeof(file.mimetype) != 'undefined'){
29550 formData.append('mimetype', file.mimetype);
29555 if(this.fireEvent('prepare', this, formData) != false){
29556 this.xhr.send(formData);
29566 * @class Roo.bootstrap.DocumentViewer
29567 * @extends Roo.bootstrap.Component
29568 * Bootstrap DocumentViewer class
29569 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29570 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29573 * Create a new DocumentViewer
29574 * @param {Object} config The config object
29577 Roo.bootstrap.DocumentViewer = function(config){
29578 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29583 * Fire after initEvent
29584 * @param {Roo.bootstrap.DocumentViewer} this
29590 * @param {Roo.bootstrap.DocumentViewer} this
29595 * Fire after download button
29596 * @param {Roo.bootstrap.DocumentViewer} this
29601 * Fire after trash button
29602 * @param {Roo.bootstrap.DocumentViewer} this
29609 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29611 showDownload : true,
29615 getAutoCreate : function()
29619 cls : 'roo-document-viewer',
29623 cls : 'roo-document-viewer-body',
29627 cls : 'roo-document-viewer-thumb',
29631 cls : 'roo-document-viewer-image'
29639 cls : 'roo-document-viewer-footer',
29642 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29646 cls : 'btn-group roo-document-viewer-download',
29650 cls : 'btn btn-default',
29651 html : '<i class="fa fa-download"></i>'
29657 cls : 'btn-group roo-document-viewer-trash',
29661 cls : 'btn btn-default',
29662 html : '<i class="fa fa-trash"></i>'
29675 initEvents : function()
29677 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29678 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29680 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29681 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29683 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29684 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29686 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29687 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29689 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29690 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29692 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29693 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29695 this.bodyEl.on('click', this.onClick, this);
29696 this.downloadBtn.on('click', this.onDownload, this);
29697 this.trashBtn.on('click', this.onTrash, this);
29699 this.downloadBtn.hide();
29700 this.trashBtn.hide();
29702 if(this.showDownload){
29703 this.downloadBtn.show();
29706 if(this.showTrash){
29707 this.trashBtn.show();
29710 if(!this.showDownload && !this.showTrash) {
29711 this.footerEl.hide();
29716 initial : function()
29718 this.fireEvent('initial', this);
29722 onClick : function(e)
29724 e.preventDefault();
29726 this.fireEvent('click', this);
29729 onDownload : function(e)
29731 e.preventDefault();
29733 this.fireEvent('download', this);
29736 onTrash : function(e)
29738 e.preventDefault();
29740 this.fireEvent('trash', this);
29752 * @class Roo.bootstrap.NavProgressBar
29753 * @extends Roo.bootstrap.Component
29754 * Bootstrap NavProgressBar class
29757 * Create a new nav progress bar
29758 * @param {Object} config The config object
29761 Roo.bootstrap.NavProgressBar = function(config){
29762 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29764 this.bullets = this.bullets || [];
29766 // Roo.bootstrap.NavProgressBar.register(this);
29770 * Fires when the active item changes
29771 * @param {Roo.bootstrap.NavProgressBar} this
29772 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29773 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29780 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29785 getAutoCreate : function()
29787 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29791 cls : 'roo-navigation-bar-group',
29795 cls : 'roo-navigation-top-bar'
29799 cls : 'roo-navigation-bullets-bar',
29803 cls : 'roo-navigation-bar'
29810 cls : 'roo-navigation-bottom-bar'
29820 initEvents: function()
29825 onRender : function(ct, position)
29827 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29829 if(this.bullets.length){
29830 Roo.each(this.bullets, function(b){
29839 addItem : function(cfg)
29841 var item = new Roo.bootstrap.NavProgressItem(cfg);
29843 item.parentId = this.id;
29844 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29847 var top = new Roo.bootstrap.Element({
29849 cls : 'roo-navigation-bar-text'
29852 var bottom = new Roo.bootstrap.Element({
29854 cls : 'roo-navigation-bar-text'
29857 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29858 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29860 var topText = new Roo.bootstrap.Element({
29862 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29865 var bottomText = new Roo.bootstrap.Element({
29867 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29870 topText.onRender(top.el, null);
29871 bottomText.onRender(bottom.el, null);
29874 item.bottomEl = bottom;
29877 this.barItems.push(item);
29882 getActive : function()
29884 var active = false;
29886 Roo.each(this.barItems, function(v){
29888 if (!v.isActive()) {
29900 setActiveItem : function(item)
29904 Roo.each(this.barItems, function(v){
29905 if (v.rid == item.rid) {
29909 if (v.isActive()) {
29910 v.setActive(false);
29915 item.setActive(true);
29917 this.fireEvent('changed', this, item, prev);
29920 getBarItem: function(rid)
29924 Roo.each(this.barItems, function(e) {
29925 if (e.rid != rid) {
29936 indexOfItem : function(item)
29940 Roo.each(this.barItems, function(v, i){
29942 if (v.rid != item.rid) {
29953 setActiveNext : function()
29955 var i = this.indexOfItem(this.getActive());
29957 if (i > this.barItems.length) {
29961 this.setActiveItem(this.barItems[i+1]);
29964 setActivePrev : function()
29966 var i = this.indexOfItem(this.getActive());
29972 this.setActiveItem(this.barItems[i-1]);
29975 format : function()
29977 if(!this.barItems.length){
29981 var width = 100 / this.barItems.length;
29983 Roo.each(this.barItems, function(i){
29984 i.el.setStyle('width', width + '%');
29985 i.topEl.el.setStyle('width', width + '%');
29986 i.bottomEl.el.setStyle('width', width + '%');
29995 * Nav Progress Item
30000 * @class Roo.bootstrap.NavProgressItem
30001 * @extends Roo.bootstrap.Component
30002 * Bootstrap NavProgressItem class
30003 * @cfg {String} rid the reference id
30004 * @cfg {Boolean} active (true|false) Is item active default false
30005 * @cfg {Boolean} disabled (true|false) Is item active default false
30006 * @cfg {String} html
30007 * @cfg {String} position (top|bottom) text position default bottom
30008 * @cfg {String} icon show icon instead of number
30011 * Create a new NavProgressItem
30012 * @param {Object} config The config object
30014 Roo.bootstrap.NavProgressItem = function(config){
30015 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30020 * The raw click event for the entire grid.
30021 * @param {Roo.bootstrap.NavProgressItem} this
30022 * @param {Roo.EventObject} e
30029 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30035 position : 'bottom',
30038 getAutoCreate : function()
30040 var iconCls = 'roo-navigation-bar-item-icon';
30042 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30046 cls: 'roo-navigation-bar-item',
30056 cfg.cls += ' active';
30059 cfg.cls += ' disabled';
30065 disable : function()
30067 this.setDisabled(true);
30070 enable : function()
30072 this.setDisabled(false);
30075 initEvents: function()
30077 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30079 this.iconEl.on('click', this.onClick, this);
30082 onClick : function(e)
30084 e.preventDefault();
30090 if(this.fireEvent('click', this, e) === false){
30094 this.parent().setActiveItem(this);
30097 isActive: function ()
30099 return this.active;
30102 setActive : function(state)
30104 if(this.active == state){
30108 this.active = state;
30111 this.el.addClass('active');
30115 this.el.removeClass('active');
30120 setDisabled : function(state)
30122 if(this.disabled == state){
30126 this.disabled = state;
30129 this.el.addClass('disabled');
30133 this.el.removeClass('disabled');
30136 tooltipEl : function()
30138 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30151 * @class Roo.bootstrap.FieldLabel
30152 * @extends Roo.bootstrap.Component
30153 * Bootstrap FieldLabel class
30154 * @cfg {String} html contents of the element
30155 * @cfg {String} tag tag of the element default label
30156 * @cfg {String} cls class of the element
30157 * @cfg {String} target label target
30158 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30159 * @cfg {String} invalidClass default "text-warning"
30160 * @cfg {String} validClass default "text-success"
30161 * @cfg {String} iconTooltip default "This field is required"
30162 * @cfg {String} indicatorpos (left|right) default left
30165 * Create a new FieldLabel
30166 * @param {Object} config The config object
30169 Roo.bootstrap.FieldLabel = function(config){
30170 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30175 * Fires after the field has been marked as invalid.
30176 * @param {Roo.form.FieldLabel} this
30177 * @param {String} msg The validation message
30182 * Fires after the field has been validated with no errors.
30183 * @param {Roo.form.FieldLabel} this
30189 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30196 invalidClass : 'has-warning',
30197 validClass : 'has-success',
30198 iconTooltip : 'This field is required',
30199 indicatorpos : 'left',
30201 getAutoCreate : function(){
30204 if (!this.allowBlank) {
30210 cls : 'roo-bootstrap-field-label ' + this.cls,
30215 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30216 tooltip : this.iconTooltip
30225 if(this.indicatorpos == 'right'){
30228 cls : 'roo-bootstrap-field-label ' + this.cls,
30237 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30238 tooltip : this.iconTooltip
30247 initEvents: function()
30249 Roo.bootstrap.Element.superclass.initEvents.call(this);
30251 this.indicator = this.indicatorEl();
30253 if(this.indicator){
30254 this.indicator.removeClass('visible');
30255 this.indicator.addClass('invisible');
30258 Roo.bootstrap.FieldLabel.register(this);
30261 indicatorEl : function()
30263 var indicator = this.el.select('i.roo-required-indicator',true).first();
30274 * Mark this field as valid
30276 markValid : function()
30278 if(this.indicator){
30279 this.indicator.removeClass('visible');
30280 this.indicator.addClass('invisible');
30283 this.el.removeClass(this.invalidClass);
30285 this.el.addClass(this.validClass);
30287 this.fireEvent('valid', this);
30291 * Mark this field as invalid
30292 * @param {String} msg The validation message
30294 markInvalid : function(msg)
30296 if(this.indicator){
30297 this.indicator.removeClass('invisible');
30298 this.indicator.addClass('visible');
30301 this.el.removeClass(this.validClass);
30303 this.el.addClass(this.invalidClass);
30305 this.fireEvent('invalid', this, msg);
30311 Roo.apply(Roo.bootstrap.FieldLabel, {
30316 * register a FieldLabel Group
30317 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30319 register : function(label)
30321 if(this.groups.hasOwnProperty(label.target)){
30325 this.groups[label.target] = label;
30329 * fetch a FieldLabel Group based on the target
30330 * @param {string} target
30331 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30333 get: function(target) {
30334 if (typeof(this.groups[target]) == 'undefined') {
30338 return this.groups[target] ;
30347 * page DateSplitField.
30353 * @class Roo.bootstrap.DateSplitField
30354 * @extends Roo.bootstrap.Component
30355 * Bootstrap DateSplitField class
30356 * @cfg {string} fieldLabel - the label associated
30357 * @cfg {Number} labelWidth set the width of label (0-12)
30358 * @cfg {String} labelAlign (top|left)
30359 * @cfg {Boolean} dayAllowBlank (true|false) default false
30360 * @cfg {Boolean} monthAllowBlank (true|false) default false
30361 * @cfg {Boolean} yearAllowBlank (true|false) default false
30362 * @cfg {string} dayPlaceholder
30363 * @cfg {string} monthPlaceholder
30364 * @cfg {string} yearPlaceholder
30365 * @cfg {string} dayFormat default 'd'
30366 * @cfg {string} monthFormat default 'm'
30367 * @cfg {string} yearFormat default 'Y'
30368 * @cfg {Number} labellg set the width of label (1-12)
30369 * @cfg {Number} labelmd set the width of label (1-12)
30370 * @cfg {Number} labelsm set the width of label (1-12)
30371 * @cfg {Number} labelxs set the width of label (1-12)
30375 * Create a new DateSplitField
30376 * @param {Object} config The config object
30379 Roo.bootstrap.DateSplitField = function(config){
30380 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30386 * getting the data of years
30387 * @param {Roo.bootstrap.DateSplitField} this
30388 * @param {Object} years
30393 * getting the data of days
30394 * @param {Roo.bootstrap.DateSplitField} this
30395 * @param {Object} days
30400 * Fires after the field has been marked as invalid.
30401 * @param {Roo.form.Field} this
30402 * @param {String} msg The validation message
30407 * Fires after the field has been validated with no errors.
30408 * @param {Roo.form.Field} this
30414 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30417 labelAlign : 'top',
30419 dayAllowBlank : false,
30420 monthAllowBlank : false,
30421 yearAllowBlank : false,
30422 dayPlaceholder : '',
30423 monthPlaceholder : '',
30424 yearPlaceholder : '',
30428 isFormField : true,
30434 getAutoCreate : function()
30438 cls : 'row roo-date-split-field-group',
30443 cls : 'form-hidden-field roo-date-split-field-group-value',
30449 var labelCls = 'col-md-12';
30450 var contentCls = 'col-md-4';
30452 if(this.fieldLabel){
30456 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30460 html : this.fieldLabel
30465 if(this.labelAlign == 'left'){
30467 if(this.labelWidth > 12){
30468 label.style = "width: " + this.labelWidth + 'px';
30471 if(this.labelWidth < 13 && this.labelmd == 0){
30472 this.labelmd = this.labelWidth;
30475 if(this.labellg > 0){
30476 labelCls = ' col-lg-' + this.labellg;
30477 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30480 if(this.labelmd > 0){
30481 labelCls = ' col-md-' + this.labelmd;
30482 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30485 if(this.labelsm > 0){
30486 labelCls = ' col-sm-' + this.labelsm;
30487 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30490 if(this.labelxs > 0){
30491 labelCls = ' col-xs-' + this.labelxs;
30492 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30496 label.cls += ' ' + labelCls;
30498 cfg.cn.push(label);
30501 Roo.each(['day', 'month', 'year'], function(t){
30504 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30511 inputEl: function ()
30513 return this.el.select('.roo-date-split-field-group-value', true).first();
30516 onRender : function(ct, position)
30520 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30522 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30524 this.dayField = new Roo.bootstrap.ComboBox({
30525 allowBlank : this.dayAllowBlank,
30526 alwaysQuery : true,
30527 displayField : 'value',
30530 forceSelection : true,
30532 placeholder : this.dayPlaceholder,
30533 selectOnFocus : true,
30534 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30535 triggerAction : 'all',
30537 valueField : 'value',
30538 store : new Roo.data.SimpleStore({
30539 data : (function() {
30541 _this.fireEvent('days', _this, days);
30544 fields : [ 'value' ]
30547 select : function (_self, record, index)
30549 _this.setValue(_this.getValue());
30554 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30556 this.monthField = new Roo.bootstrap.MonthField({
30557 after : '<i class=\"fa fa-calendar\"></i>',
30558 allowBlank : this.monthAllowBlank,
30559 placeholder : this.monthPlaceholder,
30562 render : function (_self)
30564 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30565 e.preventDefault();
30569 select : function (_self, oldvalue, newvalue)
30571 _this.setValue(_this.getValue());
30576 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30578 this.yearField = new Roo.bootstrap.ComboBox({
30579 allowBlank : this.yearAllowBlank,
30580 alwaysQuery : true,
30581 displayField : 'value',
30584 forceSelection : true,
30586 placeholder : this.yearPlaceholder,
30587 selectOnFocus : true,
30588 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30589 triggerAction : 'all',
30591 valueField : 'value',
30592 store : new Roo.data.SimpleStore({
30593 data : (function() {
30595 _this.fireEvent('years', _this, years);
30598 fields : [ 'value' ]
30601 select : function (_self, record, index)
30603 _this.setValue(_this.getValue());
30608 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30611 setValue : function(v, format)
30613 this.inputEl.dom.value = v;
30615 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30617 var d = Date.parseDate(v, f);
30624 this.setDay(d.format(this.dayFormat));
30625 this.setMonth(d.format(this.monthFormat));
30626 this.setYear(d.format(this.yearFormat));
30633 setDay : function(v)
30635 this.dayField.setValue(v);
30636 this.inputEl.dom.value = this.getValue();
30641 setMonth : function(v)
30643 this.monthField.setValue(v, true);
30644 this.inputEl.dom.value = this.getValue();
30649 setYear : function(v)
30651 this.yearField.setValue(v);
30652 this.inputEl.dom.value = this.getValue();
30657 getDay : function()
30659 return this.dayField.getValue();
30662 getMonth : function()
30664 return this.monthField.getValue();
30667 getYear : function()
30669 return this.yearField.getValue();
30672 getValue : function()
30674 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30676 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30686 this.inputEl.dom.value = '';
30691 validate : function()
30693 var d = this.dayField.validate();
30694 var m = this.monthField.validate();
30695 var y = this.yearField.validate();
30700 (!this.dayAllowBlank && !d) ||
30701 (!this.monthAllowBlank && !m) ||
30702 (!this.yearAllowBlank && !y)
30707 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30716 this.markInvalid();
30721 markValid : function()
30724 var label = this.el.select('label', true).first();
30725 var icon = this.el.select('i.fa-star', true).first();
30731 this.fireEvent('valid', this);
30735 * Mark this field as invalid
30736 * @param {String} msg The validation message
30738 markInvalid : function(msg)
30741 var label = this.el.select('label', true).first();
30742 var icon = this.el.select('i.fa-star', true).first();
30744 if(label && !icon){
30745 this.el.select('.roo-date-split-field-label', true).createChild({
30747 cls : 'text-danger fa fa-lg fa-star',
30748 tooltip : 'This field is required',
30749 style : 'margin-right:5px;'
30753 this.fireEvent('invalid', this, msg);
30756 clearInvalid : function()
30758 var label = this.el.select('label', true).first();
30759 var icon = this.el.select('i.fa-star', true).first();
30765 this.fireEvent('valid', this);
30768 getName: function()
30778 * http://masonry.desandro.com
30780 * The idea is to render all the bricks based on vertical width...
30782 * The original code extends 'outlayer' - we might need to use that....
30788 * @class Roo.bootstrap.LayoutMasonry
30789 * @extends Roo.bootstrap.Component
30790 * Bootstrap Layout Masonry class
30793 * Create a new Element
30794 * @param {Object} config The config object
30797 Roo.bootstrap.LayoutMasonry = function(config){
30799 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30803 Roo.bootstrap.LayoutMasonry.register(this);
30809 * Fire after layout the items
30810 * @param {Roo.bootstrap.LayoutMasonry} this
30811 * @param {Roo.EventObject} e
30818 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30821 * @cfg {Boolean} isLayoutInstant = no animation?
30823 isLayoutInstant : false, // needed?
30826 * @cfg {Number} boxWidth width of the columns
30831 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30836 * @cfg {Number} padWidth padding below box..
30841 * @cfg {Number} gutter gutter width..
30846 * @cfg {Number} maxCols maximum number of columns
30852 * @cfg {Boolean} isAutoInitial defalut true
30854 isAutoInitial : true,
30859 * @cfg {Boolean} isHorizontal defalut false
30861 isHorizontal : false,
30863 currentSize : null,
30869 bricks: null, //CompositeElement
30873 _isLayoutInited : false,
30875 // isAlternative : false, // only use for vertical layout...
30878 * @cfg {Number} alternativePadWidth padding below box..
30880 alternativePadWidth : 50,
30882 selectedBrick : [],
30884 getAutoCreate : function(){
30886 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30890 cls: 'blog-masonary-wrapper ' + this.cls,
30892 cls : 'mas-boxes masonary'
30899 getChildContainer: function( )
30901 if (this.boxesEl) {
30902 return this.boxesEl;
30905 this.boxesEl = this.el.select('.mas-boxes').first();
30907 return this.boxesEl;
30911 initEvents : function()
30915 if(this.isAutoInitial){
30916 Roo.log('hook children rendered');
30917 this.on('childrenrendered', function() {
30918 Roo.log('children rendered');
30924 initial : function()
30926 this.selectedBrick = [];
30928 this.currentSize = this.el.getBox(true);
30930 Roo.EventManager.onWindowResize(this.resize, this);
30932 if(!this.isAutoInitial){
30940 //this.layout.defer(500,this);
30944 resize : function()
30946 var cs = this.el.getBox(true);
30949 this.currentSize.width == cs.width &&
30950 this.currentSize.x == cs.x &&
30951 this.currentSize.height == cs.height &&
30952 this.currentSize.y == cs.y
30954 Roo.log("no change in with or X or Y");
30958 this.currentSize = cs;
30964 layout : function()
30966 this._resetLayout();
30968 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30970 this.layoutItems( isInstant );
30972 this._isLayoutInited = true;
30974 this.fireEvent('layout', this);
30978 _resetLayout : function()
30980 if(this.isHorizontal){
30981 this.horizontalMeasureColumns();
30985 this.verticalMeasureColumns();
30989 verticalMeasureColumns : function()
30991 this.getContainerWidth();
30993 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30994 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30998 var boxWidth = this.boxWidth + this.padWidth;
31000 if(this.containerWidth < this.boxWidth){
31001 boxWidth = this.containerWidth
31004 var containerWidth = this.containerWidth;
31006 var cols = Math.floor(containerWidth / boxWidth);
31008 this.cols = Math.max( cols, 1 );
31010 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31012 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31014 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31016 this.colWidth = boxWidth + avail - this.padWidth;
31018 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31019 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31022 horizontalMeasureColumns : function()
31024 this.getContainerWidth();
31026 var boxWidth = this.boxWidth;
31028 if(this.containerWidth < boxWidth){
31029 boxWidth = this.containerWidth;
31032 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31034 this.el.setHeight(boxWidth);
31038 getContainerWidth : function()
31040 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31043 layoutItems : function( isInstant )
31045 Roo.log(this.bricks);
31047 var items = Roo.apply([], this.bricks);
31049 if(this.isHorizontal){
31050 this._horizontalLayoutItems( items , isInstant );
31054 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31055 // this._verticalAlternativeLayoutItems( items , isInstant );
31059 this._verticalLayoutItems( items , isInstant );
31063 _verticalLayoutItems : function ( items , isInstant)
31065 if ( !items || !items.length ) {
31070 ['xs', 'xs', 'xs', 'tall'],
31071 ['xs', 'xs', 'tall'],
31072 ['xs', 'xs', 'sm'],
31073 ['xs', 'xs', 'xs'],
31079 ['sm', 'xs', 'xs'],
31083 ['tall', 'xs', 'xs', 'xs'],
31084 ['tall', 'xs', 'xs'],
31096 Roo.each(items, function(item, k){
31098 switch (item.size) {
31099 // these layouts take up a full box,
31110 boxes.push([item]);
31133 var filterPattern = function(box, length)
31141 var pattern = box.slice(0, length);
31145 Roo.each(pattern, function(i){
31146 format.push(i.size);
31149 Roo.each(standard, function(s){
31151 if(String(s) != String(format)){
31160 if(!match && length == 1){
31165 filterPattern(box, length - 1);
31169 queue.push(pattern);
31171 box = box.slice(length, box.length);
31173 filterPattern(box, 4);
31179 Roo.each(boxes, function(box, k){
31185 if(box.length == 1){
31190 filterPattern(box, 4);
31194 this._processVerticalLayoutQueue( queue, isInstant );
31198 // _verticalAlternativeLayoutItems : function( items , isInstant )
31200 // if ( !items || !items.length ) {
31204 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31208 _horizontalLayoutItems : function ( items , isInstant)
31210 if ( !items || !items.length || items.length < 3) {
31216 var eItems = items.slice(0, 3);
31218 items = items.slice(3, items.length);
31221 ['xs', 'xs', 'xs', 'wide'],
31222 ['xs', 'xs', 'wide'],
31223 ['xs', 'xs', 'sm'],
31224 ['xs', 'xs', 'xs'],
31230 ['sm', 'xs', 'xs'],
31234 ['wide', 'xs', 'xs', 'xs'],
31235 ['wide', 'xs', 'xs'],
31248 Roo.each(items, function(item, k){
31250 switch (item.size) {
31261 boxes.push([item]);
31285 var filterPattern = function(box, length)
31293 var pattern = box.slice(0, length);
31297 Roo.each(pattern, function(i){
31298 format.push(i.size);
31301 Roo.each(standard, function(s){
31303 if(String(s) != String(format)){
31312 if(!match && length == 1){
31317 filterPattern(box, length - 1);
31321 queue.push(pattern);
31323 box = box.slice(length, box.length);
31325 filterPattern(box, 4);
31331 Roo.each(boxes, function(box, k){
31337 if(box.length == 1){
31342 filterPattern(box, 4);
31349 var pos = this.el.getBox(true);
31353 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31355 var hit_end = false;
31357 Roo.each(queue, function(box){
31361 Roo.each(box, function(b){
31363 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31373 Roo.each(box, function(b){
31375 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31378 mx = Math.max(mx, b.x);
31382 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31386 Roo.each(box, function(b){
31388 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31402 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31405 /** Sets position of item in DOM
31406 * @param {Element} item
31407 * @param {Number} x - horizontal position
31408 * @param {Number} y - vertical position
31409 * @param {Boolean} isInstant - disables transitions
31411 _processVerticalLayoutQueue : function( queue, isInstant )
31413 var pos = this.el.getBox(true);
31418 for (var i = 0; i < this.cols; i++){
31422 Roo.each(queue, function(box, k){
31424 var col = k % this.cols;
31426 Roo.each(box, function(b,kk){
31428 b.el.position('absolute');
31430 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31431 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31433 if(b.size == 'md-left' || b.size == 'md-right'){
31434 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31435 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31438 b.el.setWidth(width);
31439 b.el.setHeight(height);
31441 b.el.select('iframe',true).setSize(width,height);
31445 for (var i = 0; i < this.cols; i++){
31447 if(maxY[i] < maxY[col]){
31452 col = Math.min(col, i);
31456 x = pos.x + col * (this.colWidth + this.padWidth);
31460 var positions = [];
31462 switch (box.length){
31464 positions = this.getVerticalOneBoxColPositions(x, y, box);
31467 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31470 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31473 positions = this.getVerticalFourBoxColPositions(x, y, box);
31479 Roo.each(box, function(b,kk){
31481 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31483 var sz = b.el.getSize();
31485 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31493 for (var i = 0; i < this.cols; i++){
31494 mY = Math.max(mY, maxY[i]);
31497 this.el.setHeight(mY - pos.y);
31501 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31503 // var pos = this.el.getBox(true);
31506 // var maxX = pos.right;
31508 // var maxHeight = 0;
31510 // Roo.each(items, function(item, k){
31514 // item.el.position('absolute');
31516 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31518 // item.el.setWidth(width);
31520 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31522 // item.el.setHeight(height);
31525 // item.el.setXY([x, y], isInstant ? false : true);
31527 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31530 // y = y + height + this.alternativePadWidth;
31532 // maxHeight = maxHeight + height + this.alternativePadWidth;
31536 // this.el.setHeight(maxHeight);
31540 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31542 var pos = this.el.getBox(true);
31547 var maxX = pos.right;
31549 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31551 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31553 Roo.each(queue, function(box, k){
31555 Roo.each(box, function(b, kk){
31557 b.el.position('absolute');
31559 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31560 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31562 if(b.size == 'md-left' || b.size == 'md-right'){
31563 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31564 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31567 b.el.setWidth(width);
31568 b.el.setHeight(height);
31576 var positions = [];
31578 switch (box.length){
31580 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31583 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31586 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31589 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31595 Roo.each(box, function(b,kk){
31597 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31599 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31607 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31609 Roo.each(eItems, function(b,k){
31611 b.size = (k == 0) ? 'sm' : 'xs';
31612 b.x = (k == 0) ? 2 : 1;
31613 b.y = (k == 0) ? 2 : 1;
31615 b.el.position('absolute');
31617 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31619 b.el.setWidth(width);
31621 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31623 b.el.setHeight(height);
31627 var positions = [];
31630 x : maxX - this.unitWidth * 2 - this.gutter,
31635 x : maxX - this.unitWidth,
31636 y : minY + (this.unitWidth + this.gutter) * 2
31640 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31644 Roo.each(eItems, function(b,k){
31646 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31652 getVerticalOneBoxColPositions : function(x, y, box)
31656 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31658 if(box[0].size == 'md-left'){
31662 if(box[0].size == 'md-right'){
31667 x : x + (this.unitWidth + this.gutter) * rand,
31674 getVerticalTwoBoxColPositions : function(x, y, box)
31678 if(box[0].size == 'xs'){
31682 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31686 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31700 x : x + (this.unitWidth + this.gutter) * 2,
31701 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31708 getVerticalThreeBoxColPositions : function(x, y, box)
31712 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31720 x : x + (this.unitWidth + this.gutter) * 1,
31725 x : x + (this.unitWidth + this.gutter) * 2,
31733 if(box[0].size == 'xs' && box[1].size == 'xs'){
31742 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31746 x : x + (this.unitWidth + this.gutter) * 1,
31760 x : x + (this.unitWidth + this.gutter) * 2,
31765 x : x + (this.unitWidth + this.gutter) * 2,
31766 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31773 getVerticalFourBoxColPositions : function(x, y, box)
31777 if(box[0].size == 'xs'){
31786 y : y + (this.unitHeight + this.gutter) * 1
31791 y : y + (this.unitHeight + this.gutter) * 2
31795 x : x + (this.unitWidth + this.gutter) * 1,
31809 x : x + (this.unitWidth + this.gutter) * 2,
31814 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31815 y : y + (this.unitHeight + this.gutter) * 1
31819 x : x + (this.unitWidth + this.gutter) * 2,
31820 y : y + (this.unitWidth + this.gutter) * 2
31827 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31831 if(box[0].size == 'md-left'){
31833 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31840 if(box[0].size == 'md-right'){
31842 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31843 y : minY + (this.unitWidth + this.gutter) * 1
31849 var rand = Math.floor(Math.random() * (4 - box[0].y));
31852 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31853 y : minY + (this.unitWidth + this.gutter) * rand
31860 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31864 if(box[0].size == 'xs'){
31867 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31872 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31873 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31881 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31886 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31887 y : minY + (this.unitWidth + this.gutter) * 2
31894 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31898 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31901 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31906 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31907 y : minY + (this.unitWidth + this.gutter) * 1
31911 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31912 y : minY + (this.unitWidth + this.gutter) * 2
31919 if(box[0].size == 'xs' && box[1].size == 'xs'){
31922 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31927 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31932 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31933 y : minY + (this.unitWidth + this.gutter) * 1
31941 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31946 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31947 y : minY + (this.unitWidth + this.gutter) * 2
31951 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31952 y : minY + (this.unitWidth + this.gutter) * 2
31959 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31963 if(box[0].size == 'xs'){
31966 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31971 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31976 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),
31981 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31982 y : minY + (this.unitWidth + this.gutter) * 1
31990 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31995 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31996 y : minY + (this.unitWidth + this.gutter) * 2
32000 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32001 y : minY + (this.unitWidth + this.gutter) * 2
32005 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),
32006 y : minY + (this.unitWidth + this.gutter) * 2
32014 * remove a Masonry Brick
32015 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32017 removeBrick : function(brick_id)
32023 for (var i = 0; i<this.bricks.length; i++) {
32024 if (this.bricks[i].id == brick_id) {
32025 this.bricks.splice(i,1);
32026 this.el.dom.removeChild(Roo.get(brick_id).dom);
32033 * adds a Masonry Brick
32034 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32036 addBrick : function(cfg)
32038 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32039 //this.register(cn);
32040 cn.parentId = this.id;
32041 cn.render(this.el);
32046 * register a Masonry Brick
32047 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32050 register : function(brick)
32052 this.bricks.push(brick);
32053 brick.masonryId = this.id;
32057 * clear all the Masonry Brick
32059 clearAll : function()
32062 //this.getChildContainer().dom.innerHTML = "";
32063 this.el.dom.innerHTML = '';
32066 getSelected : function()
32068 if (!this.selectedBrick) {
32072 return this.selectedBrick;
32076 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32080 * register a Masonry Layout
32081 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32084 register : function(layout)
32086 this.groups[layout.id] = layout;
32089 * fetch a Masonry Layout based on the masonry layout ID
32090 * @param {string} the masonry layout to add
32091 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32094 get: function(layout_id) {
32095 if (typeof(this.groups[layout_id]) == 'undefined') {
32098 return this.groups[layout_id] ;
32110 * http://masonry.desandro.com
32112 * The idea is to render all the bricks based on vertical width...
32114 * The original code extends 'outlayer' - we might need to use that....
32120 * @class Roo.bootstrap.LayoutMasonryAuto
32121 * @extends Roo.bootstrap.Component
32122 * Bootstrap Layout Masonry class
32125 * Create a new Element
32126 * @param {Object} config The config object
32129 Roo.bootstrap.LayoutMasonryAuto = function(config){
32130 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32133 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32136 * @cfg {Boolean} isFitWidth - resize the width..
32138 isFitWidth : false, // options..
32140 * @cfg {Boolean} isOriginLeft = left align?
32142 isOriginLeft : true,
32144 * @cfg {Boolean} isOriginTop = top align?
32146 isOriginTop : false,
32148 * @cfg {Boolean} isLayoutInstant = no animation?
32150 isLayoutInstant : false, // needed?
32152 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32154 isResizingContainer : true,
32156 * @cfg {Number} columnWidth width of the columns
32162 * @cfg {Number} maxCols maximum number of columns
32167 * @cfg {Number} padHeight padding below box..
32173 * @cfg {Boolean} isAutoInitial defalut true
32176 isAutoInitial : true,
32182 initialColumnWidth : 0,
32183 currentSize : null,
32185 colYs : null, // array.
32192 bricks: null, //CompositeElement
32193 cols : 0, // array?
32194 // element : null, // wrapped now this.el
32195 _isLayoutInited : null,
32198 getAutoCreate : function(){
32202 cls: 'blog-masonary-wrapper ' + this.cls,
32204 cls : 'mas-boxes masonary'
32211 getChildContainer: function( )
32213 if (this.boxesEl) {
32214 return this.boxesEl;
32217 this.boxesEl = this.el.select('.mas-boxes').first();
32219 return this.boxesEl;
32223 initEvents : function()
32227 if(this.isAutoInitial){
32228 Roo.log('hook children rendered');
32229 this.on('childrenrendered', function() {
32230 Roo.log('children rendered');
32237 initial : function()
32239 this.reloadItems();
32241 this.currentSize = this.el.getBox(true);
32243 /// was window resize... - let's see if this works..
32244 Roo.EventManager.onWindowResize(this.resize, this);
32246 if(!this.isAutoInitial){
32251 this.layout.defer(500,this);
32254 reloadItems: function()
32256 this.bricks = this.el.select('.masonry-brick', true);
32258 this.bricks.each(function(b) {
32259 //Roo.log(b.getSize());
32260 if (!b.attr('originalwidth')) {
32261 b.attr('originalwidth', b.getSize().width);
32266 Roo.log(this.bricks.elements.length);
32269 resize : function()
32272 var cs = this.el.getBox(true);
32274 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32275 Roo.log("no change in with or X");
32278 this.currentSize = cs;
32282 layout : function()
32285 this._resetLayout();
32286 //this._manageStamps();
32288 // don't animate first layout
32289 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32290 this.layoutItems( isInstant );
32292 // flag for initalized
32293 this._isLayoutInited = true;
32296 layoutItems : function( isInstant )
32298 //var items = this._getItemsForLayout( this.items );
32299 // original code supports filtering layout items.. we just ignore it..
32301 this._layoutItems( this.bricks , isInstant );
32303 this._postLayout();
32305 _layoutItems : function ( items , isInstant)
32307 //this.fireEvent( 'layout', this, items );
32310 if ( !items || !items.elements.length ) {
32311 // no items, emit event with empty array
32316 items.each(function(item) {
32317 Roo.log("layout item");
32319 // get x/y object from method
32320 var position = this._getItemLayoutPosition( item );
32322 position.item = item;
32323 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32324 queue.push( position );
32327 this._processLayoutQueue( queue );
32329 /** Sets position of item in DOM
32330 * @param {Element} item
32331 * @param {Number} x - horizontal position
32332 * @param {Number} y - vertical position
32333 * @param {Boolean} isInstant - disables transitions
32335 _processLayoutQueue : function( queue )
32337 for ( var i=0, len = queue.length; i < len; i++ ) {
32338 var obj = queue[i];
32339 obj.item.position('absolute');
32340 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32346 * Any logic you want to do after each layout,
32347 * i.e. size the container
32349 _postLayout : function()
32351 this.resizeContainer();
32354 resizeContainer : function()
32356 if ( !this.isResizingContainer ) {
32359 var size = this._getContainerSize();
32361 this.el.setSize(size.width,size.height);
32362 this.boxesEl.setSize(size.width,size.height);
32368 _resetLayout : function()
32370 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32371 this.colWidth = this.el.getWidth();
32372 //this.gutter = this.el.getWidth();
32374 this.measureColumns();
32380 this.colYs.push( 0 );
32386 measureColumns : function()
32388 this.getContainerWidth();
32389 // if columnWidth is 0, default to outerWidth of first item
32390 if ( !this.columnWidth ) {
32391 var firstItem = this.bricks.first();
32392 Roo.log(firstItem);
32393 this.columnWidth = this.containerWidth;
32394 if (firstItem && firstItem.attr('originalwidth') ) {
32395 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32397 // columnWidth fall back to item of first element
32398 Roo.log("set column width?");
32399 this.initialColumnWidth = this.columnWidth ;
32401 // if first elem has no width, default to size of container
32406 if (this.initialColumnWidth) {
32407 this.columnWidth = this.initialColumnWidth;
32412 // column width is fixed at the top - however if container width get's smaller we should
32415 // this bit calcs how man columns..
32417 var columnWidth = this.columnWidth += this.gutter;
32419 // calculate columns
32420 var containerWidth = this.containerWidth + this.gutter;
32422 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32423 // fix rounding errors, typically with gutters
32424 var excess = columnWidth - containerWidth % columnWidth;
32427 // if overshoot is less than a pixel, round up, otherwise floor it
32428 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32429 cols = Math[ mathMethod ]( cols );
32430 this.cols = Math.max( cols, 1 );
32431 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32433 // padding positioning..
32434 var totalColWidth = this.cols * this.columnWidth;
32435 var padavail = this.containerWidth - totalColWidth;
32436 // so for 2 columns - we need 3 'pads'
32438 var padNeeded = (1+this.cols) * this.padWidth;
32440 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32442 this.columnWidth += padExtra
32443 //this.padWidth = Math.floor(padavail / ( this.cols));
32445 // adjust colum width so that padding is fixed??
32447 // we have 3 columns ... total = width * 3
32448 // we have X left over... that should be used by
32450 //if (this.expandC) {
32458 getContainerWidth : function()
32460 /* // container is parent if fit width
32461 var container = this.isFitWidth ? this.element.parentNode : this.element;
32462 // check that this.size and size are there
32463 // IE8 triggers resize on body size change, so they might not be
32465 var size = getSize( container ); //FIXME
32466 this.containerWidth = size && size.innerWidth; //FIXME
32469 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32473 _getItemLayoutPosition : function( item ) // what is item?
32475 // we resize the item to our columnWidth..
32477 item.setWidth(this.columnWidth);
32478 item.autoBoxAdjust = false;
32480 var sz = item.getSize();
32482 // how many columns does this brick span
32483 var remainder = this.containerWidth % this.columnWidth;
32485 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32486 // round if off by 1 pixel, otherwise use ceil
32487 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32488 colSpan = Math.min( colSpan, this.cols );
32490 // normally this should be '1' as we dont' currently allow multi width columns..
32492 var colGroup = this._getColGroup( colSpan );
32493 // get the minimum Y value from the columns
32494 var minimumY = Math.min.apply( Math, colGroup );
32495 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32497 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32499 // position the brick
32501 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32502 y: this.currentSize.y + minimumY + this.padHeight
32506 // apply setHeight to necessary columns
32507 var setHeight = minimumY + sz.height + this.padHeight;
32508 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32510 var setSpan = this.cols + 1 - colGroup.length;
32511 for ( var i = 0; i < setSpan; i++ ) {
32512 this.colYs[ shortColIndex + i ] = setHeight ;
32519 * @param {Number} colSpan - number of columns the element spans
32520 * @returns {Array} colGroup
32522 _getColGroup : function( colSpan )
32524 if ( colSpan < 2 ) {
32525 // if brick spans only one column, use all the column Ys
32530 // how many different places could this brick fit horizontally
32531 var groupCount = this.cols + 1 - colSpan;
32532 // for each group potential horizontal position
32533 for ( var i = 0; i < groupCount; i++ ) {
32534 // make an array of colY values for that one group
32535 var groupColYs = this.colYs.slice( i, i + colSpan );
32536 // and get the max value of the array
32537 colGroup[i] = Math.max.apply( Math, groupColYs );
32542 _manageStamp : function( stamp )
32544 var stampSize = stamp.getSize();
32545 var offset = stamp.getBox();
32546 // get the columns that this stamp affects
32547 var firstX = this.isOriginLeft ? offset.x : offset.right;
32548 var lastX = firstX + stampSize.width;
32549 var firstCol = Math.floor( firstX / this.columnWidth );
32550 firstCol = Math.max( 0, firstCol );
32552 var lastCol = Math.floor( lastX / this.columnWidth );
32553 // lastCol should not go over if multiple of columnWidth #425
32554 lastCol -= lastX % this.columnWidth ? 0 : 1;
32555 lastCol = Math.min( this.cols - 1, lastCol );
32557 // set colYs to bottom of the stamp
32558 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32561 for ( var i = firstCol; i <= lastCol; i++ ) {
32562 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32567 _getContainerSize : function()
32569 this.maxY = Math.max.apply( Math, this.colYs );
32574 if ( this.isFitWidth ) {
32575 size.width = this._getContainerFitWidth();
32581 _getContainerFitWidth : function()
32583 var unusedCols = 0;
32584 // count unused columns
32587 if ( this.colYs[i] !== 0 ) {
32592 // fit container to columns that have been used
32593 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32596 needsResizeLayout : function()
32598 var previousWidth = this.containerWidth;
32599 this.getContainerWidth();
32600 return previousWidth !== this.containerWidth;
32615 * @class Roo.bootstrap.MasonryBrick
32616 * @extends Roo.bootstrap.Component
32617 * Bootstrap MasonryBrick class
32620 * Create a new MasonryBrick
32621 * @param {Object} config The config object
32624 Roo.bootstrap.MasonryBrick = function(config){
32626 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32628 Roo.bootstrap.MasonryBrick.register(this);
32634 * When a MasonryBrick is clcik
32635 * @param {Roo.bootstrap.MasonryBrick} this
32636 * @param {Roo.EventObject} e
32642 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32645 * @cfg {String} title
32649 * @cfg {String} html
32653 * @cfg {String} bgimage
32657 * @cfg {String} videourl
32661 * @cfg {String} cls
32665 * @cfg {String} href
32669 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32674 * @cfg {String} placetitle (center|bottom)
32679 * @cfg {Boolean} isFitContainer defalut true
32681 isFitContainer : true,
32684 * @cfg {Boolean} preventDefault defalut false
32686 preventDefault : false,
32689 * @cfg {Boolean} inverse defalut false
32691 maskInverse : false,
32693 getAutoCreate : function()
32695 if(!this.isFitContainer){
32696 return this.getSplitAutoCreate();
32699 var cls = 'masonry-brick masonry-brick-full';
32701 if(this.href.length){
32702 cls += ' masonry-brick-link';
32705 if(this.bgimage.length){
32706 cls += ' masonry-brick-image';
32709 if(this.maskInverse){
32710 cls += ' mask-inverse';
32713 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32714 cls += ' enable-mask';
32718 cls += ' masonry-' + this.size + '-brick';
32721 if(this.placetitle.length){
32723 switch (this.placetitle) {
32725 cls += ' masonry-center-title';
32728 cls += ' masonry-bottom-title';
32735 if(!this.html.length && !this.bgimage.length){
32736 cls += ' masonry-center-title';
32739 if(!this.html.length && this.bgimage.length){
32740 cls += ' masonry-bottom-title';
32745 cls += ' ' + this.cls;
32749 tag: (this.href.length) ? 'a' : 'div',
32754 cls: 'masonry-brick-mask'
32758 cls: 'masonry-brick-paragraph',
32764 if(this.href.length){
32765 cfg.href = this.href;
32768 var cn = cfg.cn[1].cn;
32770 if(this.title.length){
32773 cls: 'masonry-brick-title',
32778 if(this.html.length){
32781 cls: 'masonry-brick-text',
32786 if (!this.title.length && !this.html.length) {
32787 cfg.cn[1].cls += ' hide';
32790 if(this.bgimage.length){
32793 cls: 'masonry-brick-image-view',
32798 if(this.videourl.length){
32799 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32800 // youtube support only?
32803 cls: 'masonry-brick-image-view',
32806 allowfullscreen : true
32814 getSplitAutoCreate : function()
32816 var cls = 'masonry-brick masonry-brick-split';
32818 if(this.href.length){
32819 cls += ' masonry-brick-link';
32822 if(this.bgimage.length){
32823 cls += ' masonry-brick-image';
32827 cls += ' masonry-' + this.size + '-brick';
32830 switch (this.placetitle) {
32832 cls += ' masonry-center-title';
32835 cls += ' masonry-bottom-title';
32838 if(!this.bgimage.length){
32839 cls += ' masonry-center-title';
32842 if(this.bgimage.length){
32843 cls += ' masonry-bottom-title';
32849 cls += ' ' + this.cls;
32853 tag: (this.href.length) ? 'a' : 'div',
32858 cls: 'masonry-brick-split-head',
32862 cls: 'masonry-brick-paragraph',
32869 cls: 'masonry-brick-split-body',
32875 if(this.href.length){
32876 cfg.href = this.href;
32879 if(this.title.length){
32880 cfg.cn[0].cn[0].cn.push({
32882 cls: 'masonry-brick-title',
32887 if(this.html.length){
32888 cfg.cn[1].cn.push({
32890 cls: 'masonry-brick-text',
32895 if(this.bgimage.length){
32896 cfg.cn[0].cn.push({
32898 cls: 'masonry-brick-image-view',
32903 if(this.videourl.length){
32904 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32905 // youtube support only?
32906 cfg.cn[0].cn.cn.push({
32908 cls: 'masonry-brick-image-view',
32911 allowfullscreen : true
32918 initEvents: function()
32920 switch (this.size) {
32953 this.el.on('touchstart', this.onTouchStart, this);
32954 this.el.on('touchmove', this.onTouchMove, this);
32955 this.el.on('touchend', this.onTouchEnd, this);
32956 this.el.on('contextmenu', this.onContextMenu, this);
32958 this.el.on('mouseenter' ,this.enter, this);
32959 this.el.on('mouseleave', this.leave, this);
32960 this.el.on('click', this.onClick, this);
32963 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32964 this.parent().bricks.push(this);
32969 onClick: function(e, el)
32971 var time = this.endTimer - this.startTimer;
32972 // Roo.log(e.preventDefault());
32975 e.preventDefault();
32980 if(!this.preventDefault){
32984 e.preventDefault();
32986 if (this.activeClass != '') {
32987 this.selectBrick();
32990 this.fireEvent('click', this, e);
32993 enter: function(e, el)
32995 e.preventDefault();
32997 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33001 if(this.bgimage.length && this.html.length){
33002 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33006 leave: function(e, el)
33008 e.preventDefault();
33010 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33014 if(this.bgimage.length && this.html.length){
33015 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33019 onTouchStart: function(e, el)
33021 // e.preventDefault();
33023 this.touchmoved = false;
33025 if(!this.isFitContainer){
33029 if(!this.bgimage.length || !this.html.length){
33033 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33035 this.timer = new Date().getTime();
33039 onTouchMove: function(e, el)
33041 this.touchmoved = true;
33044 onContextMenu : function(e,el)
33046 e.preventDefault();
33047 e.stopPropagation();
33051 onTouchEnd: function(e, el)
33053 // e.preventDefault();
33055 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33062 if(!this.bgimage.length || !this.html.length){
33064 if(this.href.length){
33065 window.location.href = this.href;
33071 if(!this.isFitContainer){
33075 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33077 window.location.href = this.href;
33080 //selection on single brick only
33081 selectBrick : function() {
33083 if (!this.parentId) {
33087 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33088 var index = m.selectedBrick.indexOf(this.id);
33091 m.selectedBrick.splice(index,1);
33092 this.el.removeClass(this.activeClass);
33096 for(var i = 0; i < m.selectedBrick.length; i++) {
33097 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33098 b.el.removeClass(b.activeClass);
33101 m.selectedBrick = [];
33103 m.selectedBrick.push(this.id);
33104 this.el.addClass(this.activeClass);
33108 isSelected : function(){
33109 return this.el.hasClass(this.activeClass);
33114 Roo.apply(Roo.bootstrap.MasonryBrick, {
33117 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33119 * register a Masonry Brick
33120 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33123 register : function(brick)
33125 //this.groups[brick.id] = brick;
33126 this.groups.add(brick.id, brick);
33129 * fetch a masonry brick based on the masonry brick ID
33130 * @param {string} the masonry brick to add
33131 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33134 get: function(brick_id)
33136 // if (typeof(this.groups[brick_id]) == 'undefined') {
33139 // return this.groups[brick_id] ;
33141 if(this.groups.key(brick_id)) {
33142 return this.groups.key(brick_id);
33160 * @class Roo.bootstrap.Brick
33161 * @extends Roo.bootstrap.Component
33162 * Bootstrap Brick class
33165 * Create a new Brick
33166 * @param {Object} config The config object
33169 Roo.bootstrap.Brick = function(config){
33170 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33176 * When a Brick is click
33177 * @param {Roo.bootstrap.Brick} this
33178 * @param {Roo.EventObject} e
33184 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33187 * @cfg {String} title
33191 * @cfg {String} html
33195 * @cfg {String} bgimage
33199 * @cfg {String} cls
33203 * @cfg {String} href
33207 * @cfg {String} video
33211 * @cfg {Boolean} square
33215 getAutoCreate : function()
33217 var cls = 'roo-brick';
33219 if(this.href.length){
33220 cls += ' roo-brick-link';
33223 if(this.bgimage.length){
33224 cls += ' roo-brick-image';
33227 if(!this.html.length && !this.bgimage.length){
33228 cls += ' roo-brick-center-title';
33231 if(!this.html.length && this.bgimage.length){
33232 cls += ' roo-brick-bottom-title';
33236 cls += ' ' + this.cls;
33240 tag: (this.href.length) ? 'a' : 'div',
33245 cls: 'roo-brick-paragraph',
33251 if(this.href.length){
33252 cfg.href = this.href;
33255 var cn = cfg.cn[0].cn;
33257 if(this.title.length){
33260 cls: 'roo-brick-title',
33265 if(this.html.length){
33268 cls: 'roo-brick-text',
33275 if(this.bgimage.length){
33278 cls: 'roo-brick-image-view',
33286 initEvents: function()
33288 if(this.title.length || this.html.length){
33289 this.el.on('mouseenter' ,this.enter, this);
33290 this.el.on('mouseleave', this.leave, this);
33293 Roo.EventManager.onWindowResize(this.resize, this);
33295 if(this.bgimage.length){
33296 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33297 this.imageEl.on('load', this.onImageLoad, this);
33304 onImageLoad : function()
33309 resize : function()
33311 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33313 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33315 if(this.bgimage.length){
33316 var image = this.el.select('.roo-brick-image-view', true).first();
33318 image.setWidth(paragraph.getWidth());
33321 image.setHeight(paragraph.getWidth());
33324 this.el.setHeight(image.getHeight());
33325 paragraph.setHeight(image.getHeight());
33331 enter: function(e, el)
33333 e.preventDefault();
33335 if(this.bgimage.length){
33336 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33337 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33341 leave: function(e, el)
33343 e.preventDefault();
33345 if(this.bgimage.length){
33346 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33347 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33362 * @class Roo.bootstrap.NumberField
33363 * @extends Roo.bootstrap.Input
33364 * Bootstrap NumberField class
33370 * Create a new NumberField
33371 * @param {Object} config The config object
33374 Roo.bootstrap.NumberField = function(config){
33375 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33378 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33381 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33383 allowDecimals : true,
33385 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33387 decimalSeparator : ".",
33389 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33391 decimalPrecision : 2,
33393 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33395 allowNegative : true,
33398 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33402 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33404 minValue : Number.NEGATIVE_INFINITY,
33406 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33408 maxValue : Number.MAX_VALUE,
33410 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33412 minText : "The minimum value for this field is {0}",
33414 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33416 maxText : "The maximum value for this field is {0}",
33418 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33419 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33421 nanText : "{0} is not a valid number",
33423 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33425 thousandsDelimiter : false,
33427 * @cfg {String} valueAlign alignment of value
33429 valueAlign : "left",
33431 getAutoCreate : function()
33433 var hiddenInput = {
33437 cls: 'hidden-number-input'
33441 hiddenInput.name = this.name;
33446 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33448 this.name = hiddenInput.name;
33450 if(cfg.cn.length > 0) {
33451 cfg.cn.push(hiddenInput);
33458 initEvents : function()
33460 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33462 var allowed = "0123456789";
33464 if(this.allowDecimals){
33465 allowed += this.decimalSeparator;
33468 if(this.allowNegative){
33472 if(this.thousandsDelimiter) {
33476 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33478 var keyPress = function(e){
33480 var k = e.getKey();
33482 var c = e.getCharCode();
33485 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33486 allowed.indexOf(String.fromCharCode(c)) === -1
33492 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33496 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33501 this.el.on("keypress", keyPress, this);
33504 validateValue : function(value)
33507 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33511 var num = this.parseValue(value);
33514 this.markInvalid(String.format(this.nanText, value));
33518 if(num < this.minValue){
33519 this.markInvalid(String.format(this.minText, this.minValue));
33523 if(num > this.maxValue){
33524 this.markInvalid(String.format(this.maxText, this.maxValue));
33531 getValue : function()
33533 var v = this.hiddenEl().getValue();
33535 return this.fixPrecision(this.parseValue(v));
33538 parseValue : function(value)
33540 if(this.thousandsDelimiter) {
33542 r = new RegExp(",", "g");
33543 value = value.replace(r, "");
33546 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33547 return isNaN(value) ? '' : value;
33550 fixPrecision : function(value)
33552 if(this.thousandsDelimiter) {
33554 r = new RegExp(",", "g");
33555 value = value.replace(r, "");
33558 var nan = isNaN(value);
33560 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33561 return nan ? '' : value;
33563 return parseFloat(value).toFixed(this.decimalPrecision);
33566 setValue : function(v)
33568 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33574 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33576 this.inputEl().dom.value = (v == '') ? '' :
33577 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33579 if(!this.allowZero && v === '0') {
33580 this.hiddenEl().dom.value = '';
33581 this.inputEl().dom.value = '';
33588 decimalPrecisionFcn : function(v)
33590 return Math.floor(v);
33593 beforeBlur : function()
33595 var v = this.parseValue(this.getRawValue());
33597 if(v || v === 0 || v === ''){
33602 hiddenEl : function()
33604 return this.el.select('input.hidden-number-input',true).first();
33616 * @class Roo.bootstrap.DocumentSlider
33617 * @extends Roo.bootstrap.Component
33618 * Bootstrap DocumentSlider class
33621 * Create a new DocumentViewer
33622 * @param {Object} config The config object
33625 Roo.bootstrap.DocumentSlider = function(config){
33626 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33633 * Fire after initEvent
33634 * @param {Roo.bootstrap.DocumentSlider} this
33639 * Fire after update
33640 * @param {Roo.bootstrap.DocumentSlider} this
33646 * @param {Roo.bootstrap.DocumentSlider} this
33652 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33658 getAutoCreate : function()
33662 cls : 'roo-document-slider',
33666 cls : 'roo-document-slider-header',
33670 cls : 'roo-document-slider-header-title'
33676 cls : 'roo-document-slider-body',
33680 cls : 'roo-document-slider-prev',
33684 cls : 'fa fa-chevron-left'
33690 cls : 'roo-document-slider-thumb',
33694 cls : 'roo-document-slider-image'
33700 cls : 'roo-document-slider-next',
33704 cls : 'fa fa-chevron-right'
33716 initEvents : function()
33718 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33719 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33721 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33722 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33724 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33725 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33727 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33728 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33730 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33731 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33733 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33734 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33736 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33737 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33739 this.thumbEl.on('click', this.onClick, this);
33741 this.prevIndicator.on('click', this.prev, this);
33743 this.nextIndicator.on('click', this.next, this);
33747 initial : function()
33749 if(this.files.length){
33750 this.indicator = 1;
33754 this.fireEvent('initial', this);
33757 update : function()
33759 this.imageEl.attr('src', this.files[this.indicator - 1]);
33761 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33763 this.prevIndicator.show();
33765 if(this.indicator == 1){
33766 this.prevIndicator.hide();
33769 this.nextIndicator.show();
33771 if(this.indicator == this.files.length){
33772 this.nextIndicator.hide();
33775 this.thumbEl.scrollTo('top');
33777 this.fireEvent('update', this);
33780 onClick : function(e)
33782 e.preventDefault();
33784 this.fireEvent('click', this);
33789 e.preventDefault();
33791 this.indicator = Math.max(1, this.indicator - 1);
33798 e.preventDefault();
33800 this.indicator = Math.min(this.files.length, this.indicator + 1);
33814 * @class Roo.bootstrap.RadioSet
33815 * @extends Roo.bootstrap.Input
33816 * Bootstrap RadioSet class
33817 * @cfg {String} indicatorpos (left|right) default left
33818 * @cfg {Boolean} inline (true|false) inline the element (default true)
33819 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33821 * Create a new RadioSet
33822 * @param {Object} config The config object
33825 Roo.bootstrap.RadioSet = function(config){
33827 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33831 Roo.bootstrap.RadioSet.register(this);
33836 * Fires when the element is checked or unchecked.
33837 * @param {Roo.bootstrap.RadioSet} this This radio
33838 * @param {Roo.bootstrap.Radio} item The checked item
33843 * Fires when the element is click.
33844 * @param {Roo.bootstrap.RadioSet} this This radio set
33845 * @param {Roo.bootstrap.Radio} item The checked item
33846 * @param {Roo.EventObject} e The event object
33853 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33861 indicatorpos : 'left',
33863 getAutoCreate : function()
33867 cls : 'roo-radio-set-label',
33871 html : this.fieldLabel
33876 if(this.indicatorpos == 'left'){
33879 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33880 tooltip : 'This field is required'
33885 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33886 tooltip : 'This field is required'
33892 cls : 'roo-radio-set-items'
33895 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33897 if (align === 'left' && this.fieldLabel.length) {
33900 cls : "roo-radio-set-right",
33906 if(this.labelWidth > 12){
33907 label.style = "width: " + this.labelWidth + 'px';
33910 if(this.labelWidth < 13 && this.labelmd == 0){
33911 this.labelmd = this.labelWidth;
33914 if(this.labellg > 0){
33915 label.cls += ' col-lg-' + this.labellg;
33916 items.cls += ' col-lg-' + (12 - this.labellg);
33919 if(this.labelmd > 0){
33920 label.cls += ' col-md-' + this.labelmd;
33921 items.cls += ' col-md-' + (12 - this.labelmd);
33924 if(this.labelsm > 0){
33925 label.cls += ' col-sm-' + this.labelsm;
33926 items.cls += ' col-sm-' + (12 - this.labelsm);
33929 if(this.labelxs > 0){
33930 label.cls += ' col-xs-' + this.labelxs;
33931 items.cls += ' col-xs-' + (12 - this.labelxs);
33937 cls : 'roo-radio-set',
33941 cls : 'roo-radio-set-input',
33944 value : this.value ? this.value : ''
33951 if(this.weight.length){
33952 cfg.cls += ' roo-radio-' + this.weight;
33956 cfg.cls += ' roo-radio-set-inline';
33960 ['xs','sm','md','lg'].map(function(size){
33961 if (settings[size]) {
33962 cfg.cls += ' col-' + size + '-' + settings[size];
33970 initEvents : function()
33972 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33973 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33975 if(!this.fieldLabel.length){
33976 this.labelEl.hide();
33979 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33980 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33982 this.indicator = this.indicatorEl();
33984 if(this.indicator){
33985 this.indicator.addClass('invisible');
33988 this.originalValue = this.getValue();
33992 inputEl: function ()
33994 return this.el.select('.roo-radio-set-input', true).first();
33997 getChildContainer : function()
33999 return this.itemsEl;
34002 register : function(item)
34004 this.radioes.push(item);
34008 validate : function()
34010 if(this.getVisibilityEl().hasClass('hidden')){
34016 Roo.each(this.radioes, function(i){
34025 if(this.allowBlank) {
34029 if(this.disabled || valid){
34034 this.markInvalid();
34039 markValid : function()
34041 if(this.labelEl.isVisible(true)){
34042 this.indicatorEl().removeClass('visible');
34043 this.indicatorEl().addClass('invisible');
34046 this.el.removeClass([this.invalidClass, this.validClass]);
34047 this.el.addClass(this.validClass);
34049 this.fireEvent('valid', this);
34052 markInvalid : function(msg)
34054 if(this.allowBlank || this.disabled){
34058 if(this.labelEl.isVisible(true)){
34059 this.indicatorEl().removeClass('invisible');
34060 this.indicatorEl().addClass('visible');
34063 this.el.removeClass([this.invalidClass, this.validClass]);
34064 this.el.addClass(this.invalidClass);
34066 this.fireEvent('invalid', this, msg);
34070 setValue : function(v, suppressEvent)
34072 if(this.value === v){
34079 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34082 Roo.each(this.radioes, function(i){
34084 i.el.removeClass('checked');
34087 Roo.each(this.radioes, function(i){
34089 if(i.value === v || i.value.toString() === v.toString()){
34091 i.el.addClass('checked');
34093 if(suppressEvent !== true){
34094 this.fireEvent('check', this, i);
34105 clearInvalid : function(){
34107 if(!this.el || this.preventMark){
34111 this.el.removeClass([this.invalidClass]);
34113 this.fireEvent('valid', this);
34118 Roo.apply(Roo.bootstrap.RadioSet, {
34122 register : function(set)
34124 this.groups[set.name] = set;
34127 get: function(name)
34129 if (typeof(this.groups[name]) == 'undefined') {
34133 return this.groups[name] ;
34139 * Ext JS Library 1.1.1
34140 * Copyright(c) 2006-2007, Ext JS, LLC.
34142 * Originally Released Under LGPL - original licence link has changed is not relivant.
34145 * <script type="text/javascript">
34150 * @class Roo.bootstrap.SplitBar
34151 * @extends Roo.util.Observable
34152 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34156 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34157 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34158 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34159 split.minSize = 100;
34160 split.maxSize = 600;
34161 split.animate = true;
34162 split.on('moved', splitterMoved);
34165 * Create a new SplitBar
34166 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34167 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34168 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34169 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34170 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34171 position of the SplitBar).
34173 Roo.bootstrap.SplitBar = function(cfg){
34178 // dragElement : elm
34179 // resizingElement: el,
34181 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34182 // placement : Roo.bootstrap.SplitBar.LEFT ,
34183 // existingProxy ???
34186 this.el = Roo.get(cfg.dragElement, true);
34187 this.el.dom.unselectable = "on";
34189 this.resizingEl = Roo.get(cfg.resizingElement, true);
34193 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34194 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34197 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34200 * The minimum size of the resizing element. (Defaults to 0)
34206 * The maximum size of the resizing element. (Defaults to 2000)
34209 this.maxSize = 2000;
34212 * Whether to animate the transition to the new size
34215 this.animate = false;
34218 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34221 this.useShim = false;
34226 if(!cfg.existingProxy){
34228 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34230 this.proxy = Roo.get(cfg.existingProxy).dom;
34233 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34236 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34239 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34242 this.dragSpecs = {};
34245 * @private The adapter to use to positon and resize elements
34247 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34248 this.adapter.init(this);
34250 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34252 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34253 this.el.addClass("roo-splitbar-h");
34256 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34257 this.el.addClass("roo-splitbar-v");
34263 * Fires when the splitter is moved (alias for {@link #event-moved})
34264 * @param {Roo.bootstrap.SplitBar} this
34265 * @param {Number} newSize the new width or height
34270 * Fires when the splitter is moved
34271 * @param {Roo.bootstrap.SplitBar} this
34272 * @param {Number} newSize the new width or height
34276 * @event beforeresize
34277 * Fires before the splitter is dragged
34278 * @param {Roo.bootstrap.SplitBar} this
34280 "beforeresize" : true,
34282 "beforeapply" : true
34285 Roo.util.Observable.call(this);
34288 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34289 onStartProxyDrag : function(x, y){
34290 this.fireEvent("beforeresize", this);
34292 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34294 o.enableDisplayMode("block");
34295 // all splitbars share the same overlay
34296 Roo.bootstrap.SplitBar.prototype.overlay = o;
34298 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34299 this.overlay.show();
34300 Roo.get(this.proxy).setDisplayed("block");
34301 var size = this.adapter.getElementSize(this);
34302 this.activeMinSize = this.getMinimumSize();;
34303 this.activeMaxSize = this.getMaximumSize();;
34304 var c1 = size - this.activeMinSize;
34305 var c2 = Math.max(this.activeMaxSize - size, 0);
34306 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34307 this.dd.resetConstraints();
34308 this.dd.setXConstraint(
34309 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34310 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34312 this.dd.setYConstraint(0, 0);
34314 this.dd.resetConstraints();
34315 this.dd.setXConstraint(0, 0);
34316 this.dd.setYConstraint(
34317 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34318 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34321 this.dragSpecs.startSize = size;
34322 this.dragSpecs.startPoint = [x, y];
34323 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34327 * @private Called after the drag operation by the DDProxy
34329 onEndProxyDrag : function(e){
34330 Roo.get(this.proxy).setDisplayed(false);
34331 var endPoint = Roo.lib.Event.getXY(e);
34333 this.overlay.hide();
34336 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34337 newSize = this.dragSpecs.startSize +
34338 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34339 endPoint[0] - this.dragSpecs.startPoint[0] :
34340 this.dragSpecs.startPoint[0] - endPoint[0]
34343 newSize = this.dragSpecs.startSize +
34344 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34345 endPoint[1] - this.dragSpecs.startPoint[1] :
34346 this.dragSpecs.startPoint[1] - endPoint[1]
34349 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34350 if(newSize != this.dragSpecs.startSize){
34351 if(this.fireEvent('beforeapply', this, newSize) !== false){
34352 this.adapter.setElementSize(this, newSize);
34353 this.fireEvent("moved", this, newSize);
34354 this.fireEvent("resize", this, newSize);
34360 * Get the adapter this SplitBar uses
34361 * @return The adapter object
34363 getAdapter : function(){
34364 return this.adapter;
34368 * Set the adapter this SplitBar uses
34369 * @param {Object} adapter A SplitBar adapter object
34371 setAdapter : function(adapter){
34372 this.adapter = adapter;
34373 this.adapter.init(this);
34377 * Gets the minimum size for the resizing element
34378 * @return {Number} The minimum size
34380 getMinimumSize : function(){
34381 return this.minSize;
34385 * Sets the minimum size for the resizing element
34386 * @param {Number} minSize The minimum size
34388 setMinimumSize : function(minSize){
34389 this.minSize = minSize;
34393 * Gets the maximum size for the resizing element
34394 * @return {Number} The maximum size
34396 getMaximumSize : function(){
34397 return this.maxSize;
34401 * Sets the maximum size for the resizing element
34402 * @param {Number} maxSize The maximum size
34404 setMaximumSize : function(maxSize){
34405 this.maxSize = maxSize;
34409 * Sets the initialize size for the resizing element
34410 * @param {Number} size The initial size
34412 setCurrentSize : function(size){
34413 var oldAnimate = this.animate;
34414 this.animate = false;
34415 this.adapter.setElementSize(this, size);
34416 this.animate = oldAnimate;
34420 * Destroy this splitbar.
34421 * @param {Boolean} removeEl True to remove the element
34423 destroy : function(removeEl){
34425 this.shim.remove();
34428 this.proxy.parentNode.removeChild(this.proxy);
34436 * @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.
34438 Roo.bootstrap.SplitBar.createProxy = function(dir){
34439 var proxy = new Roo.Element(document.createElement("div"));
34440 proxy.unselectable();
34441 var cls = 'roo-splitbar-proxy';
34442 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34443 document.body.appendChild(proxy.dom);
34448 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34449 * Default Adapter. It assumes the splitter and resizing element are not positioned
34450 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34452 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34455 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34456 // do nothing for now
34457 init : function(s){
34461 * Called before drag operations to get the current size of the resizing element.
34462 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34464 getElementSize : function(s){
34465 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34466 return s.resizingEl.getWidth();
34468 return s.resizingEl.getHeight();
34473 * Called after drag operations to set the size of the resizing element.
34474 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34475 * @param {Number} newSize The new size to set
34476 * @param {Function} onComplete A function to be invoked when resizing is complete
34478 setElementSize : function(s, newSize, onComplete){
34479 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34481 s.resizingEl.setWidth(newSize);
34483 onComplete(s, newSize);
34486 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34491 s.resizingEl.setHeight(newSize);
34493 onComplete(s, newSize);
34496 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34503 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34504 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34505 * Adapter that moves the splitter element to align with the resized sizing element.
34506 * Used with an absolute positioned SplitBar.
34507 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34508 * document.body, make sure you assign an id to the body element.
34510 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34511 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34512 this.container = Roo.get(container);
34515 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34516 init : function(s){
34517 this.basic.init(s);
34520 getElementSize : function(s){
34521 return this.basic.getElementSize(s);
34524 setElementSize : function(s, newSize, onComplete){
34525 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34528 moveSplitter : function(s){
34529 var yes = Roo.bootstrap.SplitBar;
34530 switch(s.placement){
34532 s.el.setX(s.resizingEl.getRight());
34535 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34538 s.el.setY(s.resizingEl.getBottom());
34541 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34548 * Orientation constant - Create a vertical SplitBar
34552 Roo.bootstrap.SplitBar.VERTICAL = 1;
34555 * Orientation constant - Create a horizontal SplitBar
34559 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34562 * Placement constant - The resizing element is to the left of the splitter element
34566 Roo.bootstrap.SplitBar.LEFT = 1;
34569 * Placement constant - The resizing element is to the right of the splitter element
34573 Roo.bootstrap.SplitBar.RIGHT = 2;
34576 * Placement constant - The resizing element is positioned above the splitter element
34580 Roo.bootstrap.SplitBar.TOP = 3;
34583 * Placement constant - The resizing element is positioned under splitter element
34587 Roo.bootstrap.SplitBar.BOTTOM = 4;
34588 Roo.namespace("Roo.bootstrap.layout");/*
34590 * Ext JS Library 1.1.1
34591 * Copyright(c) 2006-2007, Ext JS, LLC.
34593 * Originally Released Under LGPL - original licence link has changed is not relivant.
34596 * <script type="text/javascript">
34600 * @class Roo.bootstrap.layout.Manager
34601 * @extends Roo.bootstrap.Component
34602 * Base class for layout managers.
34604 Roo.bootstrap.layout.Manager = function(config)
34606 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34612 /** false to disable window resize monitoring @type Boolean */
34613 this.monitorWindowResize = true;
34618 * Fires when a layout is performed.
34619 * @param {Roo.LayoutManager} this
34623 * @event regionresized
34624 * Fires when the user resizes a region.
34625 * @param {Roo.LayoutRegion} region The resized region
34626 * @param {Number} newSize The new size (width for east/west, height for north/south)
34628 "regionresized" : true,
34630 * @event regioncollapsed
34631 * Fires when a region is collapsed.
34632 * @param {Roo.LayoutRegion} region The collapsed region
34634 "regioncollapsed" : true,
34636 * @event regionexpanded
34637 * Fires when a region is expanded.
34638 * @param {Roo.LayoutRegion} region The expanded region
34640 "regionexpanded" : true
34642 this.updating = false;
34645 this.el = Roo.get(config.el);
34651 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34656 monitorWindowResize : true,
34662 onRender : function(ct, position)
34665 this.el = Roo.get(ct);
34668 //this.fireEvent('render',this);
34672 initEvents: function()
34676 // ie scrollbar fix
34677 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34678 document.body.scroll = "no";
34679 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34680 this.el.position('relative');
34682 this.id = this.el.id;
34683 this.el.addClass("roo-layout-container");
34684 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34685 if(this.el.dom != document.body ) {
34686 this.el.on('resize', this.layout,this);
34687 this.el.on('show', this.layout,this);
34693 * Returns true if this layout is currently being updated
34694 * @return {Boolean}
34696 isUpdating : function(){
34697 return this.updating;
34701 * Suspend the LayoutManager from doing auto-layouts while
34702 * making multiple add or remove calls
34704 beginUpdate : function(){
34705 this.updating = true;
34709 * Restore auto-layouts and optionally disable the manager from performing a layout
34710 * @param {Boolean} noLayout true to disable a layout update
34712 endUpdate : function(noLayout){
34713 this.updating = false;
34719 layout: function(){
34723 onRegionResized : function(region, newSize){
34724 this.fireEvent("regionresized", region, newSize);
34728 onRegionCollapsed : function(region){
34729 this.fireEvent("regioncollapsed", region);
34732 onRegionExpanded : function(region){
34733 this.fireEvent("regionexpanded", region);
34737 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34738 * performs box-model adjustments.
34739 * @return {Object} The size as an object {width: (the width), height: (the height)}
34741 getViewSize : function()
34744 if(this.el.dom != document.body){
34745 size = this.el.getSize();
34747 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34749 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34750 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34755 * Returns the Element this layout is bound to.
34756 * @return {Roo.Element}
34758 getEl : function(){
34763 * Returns the specified region.
34764 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34765 * @return {Roo.LayoutRegion}
34767 getRegion : function(target){
34768 return this.regions[target.toLowerCase()];
34771 onWindowResize : function(){
34772 if(this.monitorWindowResize){
34779 * Ext JS Library 1.1.1
34780 * Copyright(c) 2006-2007, Ext JS, LLC.
34782 * Originally Released Under LGPL - original licence link has changed is not relivant.
34785 * <script type="text/javascript">
34788 * @class Roo.bootstrap.layout.Border
34789 * @extends Roo.bootstrap.layout.Manager
34790 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34791 * please see: examples/bootstrap/nested.html<br><br>
34793 <b>The container the layout is rendered into can be either the body element or any other element.
34794 If it is not the body element, the container needs to either be an absolute positioned element,
34795 or you will need to add "position:relative" to the css of the container. You will also need to specify
34796 the container size if it is not the body element.</b>
34799 * Create a new Border
34800 * @param {Object} config Configuration options
34802 Roo.bootstrap.layout.Border = function(config){
34803 config = config || {};
34804 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34808 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34809 if(config[region]){
34810 config[region].region = region;
34811 this.addRegion(config[region]);
34817 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34819 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34821 * Creates and adds a new region if it doesn't already exist.
34822 * @param {String} target The target region key (north, south, east, west or center).
34823 * @param {Object} config The regions config object
34824 * @return {BorderLayoutRegion} The new region
34826 addRegion : function(config)
34828 if(!this.regions[config.region]){
34829 var r = this.factory(config);
34830 this.bindRegion(r);
34832 return this.regions[config.region];
34836 bindRegion : function(r){
34837 this.regions[r.config.region] = r;
34839 r.on("visibilitychange", this.layout, this);
34840 r.on("paneladded", this.layout, this);
34841 r.on("panelremoved", this.layout, this);
34842 r.on("invalidated", this.layout, this);
34843 r.on("resized", this.onRegionResized, this);
34844 r.on("collapsed", this.onRegionCollapsed, this);
34845 r.on("expanded", this.onRegionExpanded, this);
34849 * Performs a layout update.
34851 layout : function()
34853 if(this.updating) {
34857 // render all the rebions if they have not been done alreayd?
34858 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34859 if(this.regions[region] && !this.regions[region].bodyEl){
34860 this.regions[region].onRender(this.el)
34864 var size = this.getViewSize();
34865 var w = size.width;
34866 var h = size.height;
34871 //var x = 0, y = 0;
34873 var rs = this.regions;
34874 var north = rs["north"];
34875 var south = rs["south"];
34876 var west = rs["west"];
34877 var east = rs["east"];
34878 var center = rs["center"];
34879 //if(this.hideOnLayout){ // not supported anymore
34880 //c.el.setStyle("display", "none");
34882 if(north && north.isVisible()){
34883 var b = north.getBox();
34884 var m = north.getMargins();
34885 b.width = w - (m.left+m.right);
34888 centerY = b.height + b.y + m.bottom;
34889 centerH -= centerY;
34890 north.updateBox(this.safeBox(b));
34892 if(south && south.isVisible()){
34893 var b = south.getBox();
34894 var m = south.getMargins();
34895 b.width = w - (m.left+m.right);
34897 var totalHeight = (b.height + m.top + m.bottom);
34898 b.y = h - totalHeight + m.top;
34899 centerH -= totalHeight;
34900 south.updateBox(this.safeBox(b));
34902 if(west && west.isVisible()){
34903 var b = west.getBox();
34904 var m = west.getMargins();
34905 b.height = centerH - (m.top+m.bottom);
34907 b.y = centerY + m.top;
34908 var totalWidth = (b.width + m.left + m.right);
34909 centerX += totalWidth;
34910 centerW -= totalWidth;
34911 west.updateBox(this.safeBox(b));
34913 if(east && east.isVisible()){
34914 var b = east.getBox();
34915 var m = east.getMargins();
34916 b.height = centerH - (m.top+m.bottom);
34917 var totalWidth = (b.width + m.left + m.right);
34918 b.x = w - totalWidth + m.left;
34919 b.y = centerY + m.top;
34920 centerW -= totalWidth;
34921 east.updateBox(this.safeBox(b));
34924 var m = center.getMargins();
34926 x: centerX + m.left,
34927 y: centerY + m.top,
34928 width: centerW - (m.left+m.right),
34929 height: centerH - (m.top+m.bottom)
34931 //if(this.hideOnLayout){
34932 //center.el.setStyle("display", "block");
34934 center.updateBox(this.safeBox(centerBox));
34937 this.fireEvent("layout", this);
34941 safeBox : function(box){
34942 box.width = Math.max(0, box.width);
34943 box.height = Math.max(0, box.height);
34948 * Adds a ContentPanel (or subclass) to this layout.
34949 * @param {String} target The target region key (north, south, east, west or center).
34950 * @param {Roo.ContentPanel} panel The panel to add
34951 * @return {Roo.ContentPanel} The added panel
34953 add : function(target, panel){
34955 target = target.toLowerCase();
34956 return this.regions[target].add(panel);
34960 * Remove a ContentPanel (or subclass) to this layout.
34961 * @param {String} target The target region key (north, south, east, west or center).
34962 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34963 * @return {Roo.ContentPanel} The removed panel
34965 remove : function(target, panel){
34966 target = target.toLowerCase();
34967 return this.regions[target].remove(panel);
34971 * Searches all regions for a panel with the specified id
34972 * @param {String} panelId
34973 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34975 findPanel : function(panelId){
34976 var rs = this.regions;
34977 for(var target in rs){
34978 if(typeof rs[target] != "function"){
34979 var p = rs[target].getPanel(panelId);
34989 * Searches all regions for a panel with the specified id and activates (shows) it.
34990 * @param {String/ContentPanel} panelId The panels id or the panel itself
34991 * @return {Roo.ContentPanel} The shown panel or null
34993 showPanel : function(panelId) {
34994 var rs = this.regions;
34995 for(var target in rs){
34996 var r = rs[target];
34997 if(typeof r != "function"){
34998 if(r.hasPanel(panelId)){
34999 return r.showPanel(panelId);
35007 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35008 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35011 restoreState : function(provider){
35013 provider = Roo.state.Manager;
35015 var sm = new Roo.LayoutStateManager();
35016 sm.init(this, provider);
35022 * Adds a xtype elements to the layout.
35026 xtype : 'ContentPanel',
35033 xtype : 'NestedLayoutPanel',
35039 items : [ ... list of content panels or nested layout panels.. ]
35043 * @param {Object} cfg Xtype definition of item to add.
35045 addxtype : function(cfg)
35047 // basically accepts a pannel...
35048 // can accept a layout region..!?!?
35049 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35052 // theory? children can only be panels??
35054 //if (!cfg.xtype.match(/Panel$/)) {
35059 if (typeof(cfg.region) == 'undefined') {
35060 Roo.log("Failed to add Panel, region was not set");
35064 var region = cfg.region;
35070 xitems = cfg.items;
35077 case 'Content': // ContentPanel (el, cfg)
35078 case 'Scroll': // ContentPanel (el, cfg)
35080 cfg.autoCreate = true;
35081 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35083 // var el = this.el.createChild();
35084 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35087 this.add(region, ret);
35091 case 'TreePanel': // our new panel!
35092 cfg.el = this.el.createChild();
35093 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35094 this.add(region, ret);
35099 // create a new Layout (which is a Border Layout...
35101 var clayout = cfg.layout;
35102 clayout.el = this.el.createChild();
35103 clayout.items = clayout.items || [];
35107 // replace this exitems with the clayout ones..
35108 xitems = clayout.items;
35110 // force background off if it's in center...
35111 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35112 cfg.background = false;
35114 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35117 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35118 //console.log('adding nested layout panel ' + cfg.toSource());
35119 this.add(region, ret);
35120 nb = {}; /// find first...
35125 // needs grid and region
35127 //var el = this.getRegion(region).el.createChild();
35129 *var el = this.el.createChild();
35130 // create the grid first...
35131 cfg.grid.container = el;
35132 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35135 if (region == 'center' && this.active ) {
35136 cfg.background = false;
35139 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35141 this.add(region, ret);
35143 if (cfg.background) {
35144 // render grid on panel activation (if panel background)
35145 ret.on('activate', function(gp) {
35146 if (!gp.grid.rendered) {
35147 // gp.grid.render(el);
35151 // cfg.grid.render(el);
35157 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35158 // it was the old xcomponent building that caused this before.
35159 // espeically if border is the top element in the tree.
35169 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35171 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35172 this.add(region, ret);
35176 throw "Can not add '" + cfg.xtype + "' to Border";
35182 this.beginUpdate();
35186 Roo.each(xitems, function(i) {
35187 region = nb && i.region ? i.region : false;
35189 var add = ret.addxtype(i);
35192 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35193 if (!i.background) {
35194 abn[region] = nb[region] ;
35201 // make the last non-background panel active..
35202 //if (nb) { Roo.log(abn); }
35205 for(var r in abn) {
35206 region = this.getRegion(r);
35208 // tried using nb[r], but it does not work..
35210 region.showPanel(abn[r]);
35221 factory : function(cfg)
35224 var validRegions = Roo.bootstrap.layout.Border.regions;
35226 var target = cfg.region;
35229 var r = Roo.bootstrap.layout;
35233 return new r.North(cfg);
35235 return new r.South(cfg);
35237 return new r.East(cfg);
35239 return new r.West(cfg);
35241 return new r.Center(cfg);
35243 throw 'Layout region "'+target+'" not supported.';
35250 * Ext JS Library 1.1.1
35251 * Copyright(c) 2006-2007, Ext JS, LLC.
35253 * Originally Released Under LGPL - original licence link has changed is not relivant.
35256 * <script type="text/javascript">
35260 * @class Roo.bootstrap.layout.Basic
35261 * @extends Roo.util.Observable
35262 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35263 * and does not have a titlebar, tabs or any other features. All it does is size and position
35264 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35265 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35266 * @cfg {string} region the region that it inhabits..
35267 * @cfg {bool} skipConfig skip config?
35271 Roo.bootstrap.layout.Basic = function(config){
35273 this.mgr = config.mgr;
35275 this.position = config.region;
35277 var skipConfig = config.skipConfig;
35281 * @scope Roo.BasicLayoutRegion
35285 * @event beforeremove
35286 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35287 * @param {Roo.LayoutRegion} this
35288 * @param {Roo.ContentPanel} panel The panel
35289 * @param {Object} e The cancel event object
35291 "beforeremove" : true,
35293 * @event invalidated
35294 * Fires when the layout for this region is changed.
35295 * @param {Roo.LayoutRegion} this
35297 "invalidated" : true,
35299 * @event visibilitychange
35300 * Fires when this region is shown or hidden
35301 * @param {Roo.LayoutRegion} this
35302 * @param {Boolean} visibility true or false
35304 "visibilitychange" : true,
35306 * @event paneladded
35307 * Fires when a panel is added.
35308 * @param {Roo.LayoutRegion} this
35309 * @param {Roo.ContentPanel} panel The panel
35311 "paneladded" : true,
35313 * @event panelremoved
35314 * Fires when a panel is removed.
35315 * @param {Roo.LayoutRegion} this
35316 * @param {Roo.ContentPanel} panel The panel
35318 "panelremoved" : true,
35320 * @event beforecollapse
35321 * Fires when this region before collapse.
35322 * @param {Roo.LayoutRegion} this
35324 "beforecollapse" : true,
35327 * Fires when this region is collapsed.
35328 * @param {Roo.LayoutRegion} this
35330 "collapsed" : true,
35333 * Fires when this region is expanded.
35334 * @param {Roo.LayoutRegion} this
35339 * Fires when this region is slid into view.
35340 * @param {Roo.LayoutRegion} this
35342 "slideshow" : true,
35345 * Fires when this region slides out of view.
35346 * @param {Roo.LayoutRegion} this
35348 "slidehide" : true,
35350 * @event panelactivated
35351 * Fires when a panel is activated.
35352 * @param {Roo.LayoutRegion} this
35353 * @param {Roo.ContentPanel} panel The activated panel
35355 "panelactivated" : true,
35358 * Fires when the user resizes this region.
35359 * @param {Roo.LayoutRegion} this
35360 * @param {Number} newSize The new size (width for east/west, height for north/south)
35364 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35365 this.panels = new Roo.util.MixedCollection();
35366 this.panels.getKey = this.getPanelId.createDelegate(this);
35368 this.activePanel = null;
35369 // ensure listeners are added...
35371 if (config.listeners || config.events) {
35372 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35373 listeners : config.listeners || {},
35374 events : config.events || {}
35378 if(skipConfig !== true){
35379 this.applyConfig(config);
35383 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35385 getPanelId : function(p){
35389 applyConfig : function(config){
35390 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35391 this.config = config;
35396 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35397 * the width, for horizontal (north, south) the height.
35398 * @param {Number} newSize The new width or height
35400 resizeTo : function(newSize){
35401 var el = this.el ? this.el :
35402 (this.activePanel ? this.activePanel.getEl() : null);
35404 switch(this.position){
35407 el.setWidth(newSize);
35408 this.fireEvent("resized", this, newSize);
35412 el.setHeight(newSize);
35413 this.fireEvent("resized", this, newSize);
35419 getBox : function(){
35420 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35423 getMargins : function(){
35424 return this.margins;
35427 updateBox : function(box){
35429 var el = this.activePanel.getEl();
35430 el.dom.style.left = box.x + "px";
35431 el.dom.style.top = box.y + "px";
35432 this.activePanel.setSize(box.width, box.height);
35436 * Returns the container element for this region.
35437 * @return {Roo.Element}
35439 getEl : function(){
35440 return this.activePanel;
35444 * Returns true if this region is currently visible.
35445 * @return {Boolean}
35447 isVisible : function(){
35448 return this.activePanel ? true : false;
35451 setActivePanel : function(panel){
35452 panel = this.getPanel(panel);
35453 if(this.activePanel && this.activePanel != panel){
35454 this.activePanel.setActiveState(false);
35455 this.activePanel.getEl().setLeftTop(-10000,-10000);
35457 this.activePanel = panel;
35458 panel.setActiveState(true);
35460 panel.setSize(this.box.width, this.box.height);
35462 this.fireEvent("panelactivated", this, panel);
35463 this.fireEvent("invalidated");
35467 * Show the specified panel.
35468 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35469 * @return {Roo.ContentPanel} The shown panel or null
35471 showPanel : function(panel){
35472 panel = this.getPanel(panel);
35474 this.setActivePanel(panel);
35480 * Get the active panel for this region.
35481 * @return {Roo.ContentPanel} The active panel or null
35483 getActivePanel : function(){
35484 return this.activePanel;
35488 * Add the passed ContentPanel(s)
35489 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35490 * @return {Roo.ContentPanel} The panel added (if only one was added)
35492 add : function(panel){
35493 if(arguments.length > 1){
35494 for(var i = 0, len = arguments.length; i < len; i++) {
35495 this.add(arguments[i]);
35499 if(this.hasPanel(panel)){
35500 this.showPanel(panel);
35503 var el = panel.getEl();
35504 if(el.dom.parentNode != this.mgr.el.dom){
35505 this.mgr.el.dom.appendChild(el.dom);
35507 if(panel.setRegion){
35508 panel.setRegion(this);
35510 this.panels.add(panel);
35511 el.setStyle("position", "absolute");
35512 if(!panel.background){
35513 this.setActivePanel(panel);
35514 if(this.config.initialSize && this.panels.getCount()==1){
35515 this.resizeTo(this.config.initialSize);
35518 this.fireEvent("paneladded", this, panel);
35523 * Returns true if the panel is in this region.
35524 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35525 * @return {Boolean}
35527 hasPanel : function(panel){
35528 if(typeof panel == "object"){ // must be panel obj
35529 panel = panel.getId();
35531 return this.getPanel(panel) ? true : false;
35535 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35536 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35537 * @param {Boolean} preservePanel Overrides the config preservePanel option
35538 * @return {Roo.ContentPanel} The panel that was removed
35540 remove : function(panel, preservePanel){
35541 panel = this.getPanel(panel);
35546 this.fireEvent("beforeremove", this, panel, e);
35547 if(e.cancel === true){
35550 var panelId = panel.getId();
35551 this.panels.removeKey(panelId);
35556 * Returns the panel specified or null if it's not in this region.
35557 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35558 * @return {Roo.ContentPanel}
35560 getPanel : function(id){
35561 if(typeof id == "object"){ // must be panel obj
35564 return this.panels.get(id);
35568 * Returns this regions position (north/south/east/west/center).
35571 getPosition: function(){
35572 return this.position;
35576 * Ext JS Library 1.1.1
35577 * Copyright(c) 2006-2007, Ext JS, LLC.
35579 * Originally Released Under LGPL - original licence link has changed is not relivant.
35582 * <script type="text/javascript">
35586 * @class Roo.bootstrap.layout.Region
35587 * @extends Roo.bootstrap.layout.Basic
35588 * This class represents a region in a layout manager.
35590 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35591 * @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})
35592 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35593 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35594 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35595 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35596 * @cfg {String} title The title for the region (overrides panel titles)
35597 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35598 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35599 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35600 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35601 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35602 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35603 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35604 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35605 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35606 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35608 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35609 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35610 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35611 * @cfg {Number} width For East/West panels
35612 * @cfg {Number} height For North/South panels
35613 * @cfg {Boolean} split To show the splitter
35614 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35616 * @cfg {string} cls Extra CSS classes to add to region
35618 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35619 * @cfg {string} region the region that it inhabits..
35622 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35623 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35625 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35626 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35627 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35629 Roo.bootstrap.layout.Region = function(config)
35631 this.applyConfig(config);
35633 var mgr = config.mgr;
35634 var pos = config.region;
35635 config.skipConfig = true;
35636 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35639 this.onRender(mgr.el);
35642 this.visible = true;
35643 this.collapsed = false;
35644 this.unrendered_panels = [];
35647 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35649 position: '', // set by wrapper (eg. north/south etc..)
35650 unrendered_panels : null, // unrendered panels.
35651 createBody : function(){
35652 /** This region's body element
35653 * @type Roo.Element */
35654 this.bodyEl = this.el.createChild({
35656 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35660 onRender: function(ctr, pos)
35662 var dh = Roo.DomHelper;
35663 /** This region's container element
35664 * @type Roo.Element */
35665 this.el = dh.append(ctr.dom, {
35667 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35669 /** This region's title element
35670 * @type Roo.Element */
35672 this.titleEl = dh.append(this.el.dom,
35675 unselectable: "on",
35676 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35678 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35679 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35682 this.titleEl.enableDisplayMode();
35683 /** This region's title text element
35684 * @type HTMLElement */
35685 this.titleTextEl = this.titleEl.dom.firstChild;
35686 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35688 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35689 this.closeBtn.enableDisplayMode();
35690 this.closeBtn.on("click", this.closeClicked, this);
35691 this.closeBtn.hide();
35693 this.createBody(this.config);
35694 if(this.config.hideWhenEmpty){
35696 this.on("paneladded", this.validateVisibility, this);
35697 this.on("panelremoved", this.validateVisibility, this);
35699 if(this.autoScroll){
35700 this.bodyEl.setStyle("overflow", "auto");
35702 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35704 //if(c.titlebar !== false){
35705 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35706 this.titleEl.hide();
35708 this.titleEl.show();
35709 if(this.config.title){
35710 this.titleTextEl.innerHTML = this.config.title;
35714 if(this.config.collapsed){
35715 this.collapse(true);
35717 if(this.config.hidden){
35721 if (this.unrendered_panels && this.unrendered_panels.length) {
35722 for (var i =0;i< this.unrendered_panels.length; i++) {
35723 this.add(this.unrendered_panels[i]);
35725 this.unrendered_panels = null;
35731 applyConfig : function(c)
35734 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35735 var dh = Roo.DomHelper;
35736 if(c.titlebar !== false){
35737 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35738 this.collapseBtn.on("click", this.collapse, this);
35739 this.collapseBtn.enableDisplayMode();
35741 if(c.showPin === true || this.showPin){
35742 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35743 this.stickBtn.enableDisplayMode();
35744 this.stickBtn.on("click", this.expand, this);
35745 this.stickBtn.hide();
35750 /** This region's collapsed element
35751 * @type Roo.Element */
35754 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35755 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35758 if(c.floatable !== false){
35759 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35760 this.collapsedEl.on("click", this.collapseClick, this);
35763 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35764 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35765 id: "message", unselectable: "on", style:{"float":"left"}});
35766 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35768 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35769 this.expandBtn.on("click", this.expand, this);
35773 if(this.collapseBtn){
35774 this.collapseBtn.setVisible(c.collapsible == true);
35777 this.cmargins = c.cmargins || this.cmargins ||
35778 (this.position == "west" || this.position == "east" ?
35779 {top: 0, left: 2, right:2, bottom: 0} :
35780 {top: 2, left: 0, right:0, bottom: 2});
35782 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35785 this.bottomTabs = c.tabPosition != "top";
35787 this.autoScroll = c.autoScroll || false;
35792 this.duration = c.duration || .30;
35793 this.slideDuration = c.slideDuration || .45;
35798 * Returns true if this region is currently visible.
35799 * @return {Boolean}
35801 isVisible : function(){
35802 return this.visible;
35806 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35807 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35809 //setCollapsedTitle : function(title){
35810 // title = title || " ";
35811 // if(this.collapsedTitleTextEl){
35812 // this.collapsedTitleTextEl.innerHTML = title;
35816 getBox : function(){
35818 // if(!this.collapsed){
35819 b = this.el.getBox(false, true);
35821 // b = this.collapsedEl.getBox(false, true);
35826 getMargins : function(){
35827 return this.margins;
35828 //return this.collapsed ? this.cmargins : this.margins;
35831 highlight : function(){
35832 this.el.addClass("x-layout-panel-dragover");
35835 unhighlight : function(){
35836 this.el.removeClass("x-layout-panel-dragover");
35839 updateBox : function(box)
35841 if (!this.bodyEl) {
35842 return; // not rendered yet..
35846 if(!this.collapsed){
35847 this.el.dom.style.left = box.x + "px";
35848 this.el.dom.style.top = box.y + "px";
35849 this.updateBody(box.width, box.height);
35851 this.collapsedEl.dom.style.left = box.x + "px";
35852 this.collapsedEl.dom.style.top = box.y + "px";
35853 this.collapsedEl.setSize(box.width, box.height);
35856 this.tabs.autoSizeTabs();
35860 updateBody : function(w, h)
35863 this.el.setWidth(w);
35864 w -= this.el.getBorderWidth("rl");
35865 if(this.config.adjustments){
35866 w += this.config.adjustments[0];
35869 if(h !== null && h > 0){
35870 this.el.setHeight(h);
35871 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35872 h -= this.el.getBorderWidth("tb");
35873 if(this.config.adjustments){
35874 h += this.config.adjustments[1];
35876 this.bodyEl.setHeight(h);
35878 h = this.tabs.syncHeight(h);
35881 if(this.panelSize){
35882 w = w !== null ? w : this.panelSize.width;
35883 h = h !== null ? h : this.panelSize.height;
35885 if(this.activePanel){
35886 var el = this.activePanel.getEl();
35887 w = w !== null ? w : el.getWidth();
35888 h = h !== null ? h : el.getHeight();
35889 this.panelSize = {width: w, height: h};
35890 this.activePanel.setSize(w, h);
35892 if(Roo.isIE && this.tabs){
35893 this.tabs.el.repaint();
35898 * Returns the container element for this region.
35899 * @return {Roo.Element}
35901 getEl : function(){
35906 * Hides this region.
35909 //if(!this.collapsed){
35910 this.el.dom.style.left = "-2000px";
35913 // this.collapsedEl.dom.style.left = "-2000px";
35914 // this.collapsedEl.hide();
35916 this.visible = false;
35917 this.fireEvent("visibilitychange", this, false);
35921 * Shows this region if it was previously hidden.
35924 //if(!this.collapsed){
35927 // this.collapsedEl.show();
35929 this.visible = true;
35930 this.fireEvent("visibilitychange", this, true);
35933 closeClicked : function(){
35934 if(this.activePanel){
35935 this.remove(this.activePanel);
35939 collapseClick : function(e){
35941 e.stopPropagation();
35944 e.stopPropagation();
35950 * Collapses this region.
35951 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35954 collapse : function(skipAnim, skipCheck = false){
35955 if(this.collapsed) {
35959 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35961 this.collapsed = true;
35963 this.split.el.hide();
35965 if(this.config.animate && skipAnim !== true){
35966 this.fireEvent("invalidated", this);
35967 this.animateCollapse();
35969 this.el.setLocation(-20000,-20000);
35971 this.collapsedEl.show();
35972 this.fireEvent("collapsed", this);
35973 this.fireEvent("invalidated", this);
35979 animateCollapse : function(){
35984 * Expands this region if it was previously collapsed.
35985 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35986 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35989 expand : function(e, skipAnim){
35991 e.stopPropagation();
35993 if(!this.collapsed || this.el.hasActiveFx()) {
35997 this.afterSlideIn();
36000 this.collapsed = false;
36001 if(this.config.animate && skipAnim !== true){
36002 this.animateExpand();
36006 this.split.el.show();
36008 this.collapsedEl.setLocation(-2000,-2000);
36009 this.collapsedEl.hide();
36010 this.fireEvent("invalidated", this);
36011 this.fireEvent("expanded", this);
36015 animateExpand : function(){
36019 initTabs : function()
36021 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36023 var ts = new Roo.bootstrap.panel.Tabs({
36024 el: this.bodyEl.dom,
36025 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36026 disableTooltips: this.config.disableTabTips,
36027 toolbar : this.config.toolbar
36030 if(this.config.hideTabs){
36031 ts.stripWrap.setDisplayed(false);
36034 ts.resizeTabs = this.config.resizeTabs === true;
36035 ts.minTabWidth = this.config.minTabWidth || 40;
36036 ts.maxTabWidth = this.config.maxTabWidth || 250;
36037 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36038 ts.monitorResize = false;
36039 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36040 ts.bodyEl.addClass('roo-layout-tabs-body');
36041 this.panels.each(this.initPanelAsTab, this);
36044 initPanelAsTab : function(panel){
36045 var ti = this.tabs.addTab(
36049 this.config.closeOnTab && panel.isClosable(),
36052 if(panel.tabTip !== undefined){
36053 ti.setTooltip(panel.tabTip);
36055 ti.on("activate", function(){
36056 this.setActivePanel(panel);
36059 if(this.config.closeOnTab){
36060 ti.on("beforeclose", function(t, e){
36062 this.remove(panel);
36066 panel.tabItem = ti;
36071 updatePanelTitle : function(panel, title)
36073 if(this.activePanel == panel){
36074 this.updateTitle(title);
36077 var ti = this.tabs.getTab(panel.getEl().id);
36079 if(panel.tabTip !== undefined){
36080 ti.setTooltip(panel.tabTip);
36085 updateTitle : function(title){
36086 if(this.titleTextEl && !this.config.title){
36087 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36091 setActivePanel : function(panel)
36093 panel = this.getPanel(panel);
36094 if(this.activePanel && this.activePanel != panel){
36095 if(this.activePanel.setActiveState(false) === false){
36099 this.activePanel = panel;
36100 panel.setActiveState(true);
36101 if(this.panelSize){
36102 panel.setSize(this.panelSize.width, this.panelSize.height);
36105 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36107 this.updateTitle(panel.getTitle());
36109 this.fireEvent("invalidated", this);
36111 this.fireEvent("panelactivated", this, panel);
36115 * Shows the specified panel.
36116 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36117 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36119 showPanel : function(panel)
36121 panel = this.getPanel(panel);
36124 var tab = this.tabs.getTab(panel.getEl().id);
36125 if(tab.isHidden()){
36126 this.tabs.unhideTab(tab.id);
36130 this.setActivePanel(panel);
36137 * Get the active panel for this region.
36138 * @return {Roo.ContentPanel} The active panel or null
36140 getActivePanel : function(){
36141 return this.activePanel;
36144 validateVisibility : function(){
36145 if(this.panels.getCount() < 1){
36146 this.updateTitle(" ");
36147 this.closeBtn.hide();
36150 if(!this.isVisible()){
36157 * Adds the passed ContentPanel(s) to this region.
36158 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36159 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36161 add : function(panel)
36163 if(arguments.length > 1){
36164 for(var i = 0, len = arguments.length; i < len; i++) {
36165 this.add(arguments[i]);
36170 // if we have not been rendered yet, then we can not really do much of this..
36171 if (!this.bodyEl) {
36172 this.unrendered_panels.push(panel);
36179 if(this.hasPanel(panel)){
36180 this.showPanel(panel);
36183 panel.setRegion(this);
36184 this.panels.add(panel);
36185 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36186 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36187 // and hide them... ???
36188 this.bodyEl.dom.appendChild(panel.getEl().dom);
36189 if(panel.background !== true){
36190 this.setActivePanel(panel);
36192 this.fireEvent("paneladded", this, panel);
36199 this.initPanelAsTab(panel);
36203 if(panel.background !== true){
36204 this.tabs.activate(panel.getEl().id);
36206 this.fireEvent("paneladded", this, panel);
36211 * Hides the tab for the specified panel.
36212 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36214 hidePanel : function(panel){
36215 if(this.tabs && (panel = this.getPanel(panel))){
36216 this.tabs.hideTab(panel.getEl().id);
36221 * Unhides the tab for a previously hidden panel.
36222 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36224 unhidePanel : function(panel){
36225 if(this.tabs && (panel = this.getPanel(panel))){
36226 this.tabs.unhideTab(panel.getEl().id);
36230 clearPanels : function(){
36231 while(this.panels.getCount() > 0){
36232 this.remove(this.panels.first());
36237 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36238 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36239 * @param {Boolean} preservePanel Overrides the config preservePanel option
36240 * @return {Roo.ContentPanel} The panel that was removed
36242 remove : function(panel, preservePanel)
36244 panel = this.getPanel(panel);
36249 this.fireEvent("beforeremove", this, panel, e);
36250 if(e.cancel === true){
36253 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36254 var panelId = panel.getId();
36255 this.panels.removeKey(panelId);
36257 document.body.appendChild(panel.getEl().dom);
36260 this.tabs.removeTab(panel.getEl().id);
36261 }else if (!preservePanel){
36262 this.bodyEl.dom.removeChild(panel.getEl().dom);
36264 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36265 var p = this.panels.first();
36266 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36267 tempEl.appendChild(p.getEl().dom);
36268 this.bodyEl.update("");
36269 this.bodyEl.dom.appendChild(p.getEl().dom);
36271 this.updateTitle(p.getTitle());
36273 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36274 this.setActivePanel(p);
36276 panel.setRegion(null);
36277 if(this.activePanel == panel){
36278 this.activePanel = null;
36280 if(this.config.autoDestroy !== false && preservePanel !== true){
36281 try{panel.destroy();}catch(e){}
36283 this.fireEvent("panelremoved", this, panel);
36288 * Returns the TabPanel component used by this region
36289 * @return {Roo.TabPanel}
36291 getTabs : function(){
36295 createTool : function(parentEl, className){
36296 var btn = Roo.DomHelper.append(parentEl, {
36298 cls: "x-layout-tools-button",
36301 cls: "roo-layout-tools-button-inner " + className,
36305 btn.addClassOnOver("roo-layout-tools-button-over");
36310 * Ext JS Library 1.1.1
36311 * Copyright(c) 2006-2007, Ext JS, LLC.
36313 * Originally Released Under LGPL - original licence link has changed is not relivant.
36316 * <script type="text/javascript">
36322 * @class Roo.SplitLayoutRegion
36323 * @extends Roo.LayoutRegion
36324 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36326 Roo.bootstrap.layout.Split = function(config){
36327 this.cursor = config.cursor;
36328 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36331 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36333 splitTip : "Drag to resize.",
36334 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36335 useSplitTips : false,
36337 applyConfig : function(config){
36338 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36341 onRender : function(ctr,pos) {
36343 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36344 if(!this.config.split){
36349 var splitEl = Roo.DomHelper.append(ctr.dom, {
36351 id: this.el.id + "-split",
36352 cls: "roo-layout-split roo-layout-split-"+this.position,
36355 /** The SplitBar for this region
36356 * @type Roo.SplitBar */
36357 // does not exist yet...
36358 Roo.log([this.position, this.orientation]);
36360 this.split = new Roo.bootstrap.SplitBar({
36361 dragElement : splitEl,
36362 resizingElement: this.el,
36363 orientation : this.orientation
36366 this.split.on("moved", this.onSplitMove, this);
36367 this.split.useShim = this.config.useShim === true;
36368 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36369 if(this.useSplitTips){
36370 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36372 //if(config.collapsible){
36373 // this.split.el.on("dblclick", this.collapse, this);
36376 if(typeof this.config.minSize != "undefined"){
36377 this.split.minSize = this.config.minSize;
36379 if(typeof this.config.maxSize != "undefined"){
36380 this.split.maxSize = this.config.maxSize;
36382 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36383 this.hideSplitter();
36388 getHMaxSize : function(){
36389 var cmax = this.config.maxSize || 10000;
36390 var center = this.mgr.getRegion("center");
36391 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36394 getVMaxSize : function(){
36395 var cmax = this.config.maxSize || 10000;
36396 var center = this.mgr.getRegion("center");
36397 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36400 onSplitMove : function(split, newSize){
36401 this.fireEvent("resized", this, newSize);
36405 * Returns the {@link Roo.SplitBar} for this region.
36406 * @return {Roo.SplitBar}
36408 getSplitBar : function(){
36413 this.hideSplitter();
36414 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36417 hideSplitter : function(){
36419 this.split.el.setLocation(-2000,-2000);
36420 this.split.el.hide();
36426 this.split.el.show();
36428 Roo.bootstrap.layout.Split.superclass.show.call(this);
36431 beforeSlide: function(){
36432 if(Roo.isGecko){// firefox overflow auto bug workaround
36433 this.bodyEl.clip();
36435 this.tabs.bodyEl.clip();
36437 if(this.activePanel){
36438 this.activePanel.getEl().clip();
36440 if(this.activePanel.beforeSlide){
36441 this.activePanel.beforeSlide();
36447 afterSlide : function(){
36448 if(Roo.isGecko){// firefox overflow auto bug workaround
36449 this.bodyEl.unclip();
36451 this.tabs.bodyEl.unclip();
36453 if(this.activePanel){
36454 this.activePanel.getEl().unclip();
36455 if(this.activePanel.afterSlide){
36456 this.activePanel.afterSlide();
36462 initAutoHide : function(){
36463 if(this.autoHide !== false){
36464 if(!this.autoHideHd){
36465 var st = new Roo.util.DelayedTask(this.slideIn, this);
36466 this.autoHideHd = {
36467 "mouseout": function(e){
36468 if(!e.within(this.el, true)){
36472 "mouseover" : function(e){
36478 this.el.on(this.autoHideHd);
36482 clearAutoHide : function(){
36483 if(this.autoHide !== false){
36484 this.el.un("mouseout", this.autoHideHd.mouseout);
36485 this.el.un("mouseover", this.autoHideHd.mouseover);
36489 clearMonitor : function(){
36490 Roo.get(document).un("click", this.slideInIf, this);
36493 // these names are backwards but not changed for compat
36494 slideOut : function(){
36495 if(this.isSlid || this.el.hasActiveFx()){
36498 this.isSlid = true;
36499 if(this.collapseBtn){
36500 this.collapseBtn.hide();
36502 this.closeBtnState = this.closeBtn.getStyle('display');
36503 this.closeBtn.hide();
36505 this.stickBtn.show();
36508 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36509 this.beforeSlide();
36510 this.el.setStyle("z-index", 10001);
36511 this.el.slideIn(this.getSlideAnchor(), {
36512 callback: function(){
36514 this.initAutoHide();
36515 Roo.get(document).on("click", this.slideInIf, this);
36516 this.fireEvent("slideshow", this);
36523 afterSlideIn : function(){
36524 this.clearAutoHide();
36525 this.isSlid = false;
36526 this.clearMonitor();
36527 this.el.setStyle("z-index", "");
36528 if(this.collapseBtn){
36529 this.collapseBtn.show();
36531 this.closeBtn.setStyle('display', this.closeBtnState);
36533 this.stickBtn.hide();
36535 this.fireEvent("slidehide", this);
36538 slideIn : function(cb){
36539 if(!this.isSlid || this.el.hasActiveFx()){
36543 this.isSlid = false;
36544 this.beforeSlide();
36545 this.el.slideOut(this.getSlideAnchor(), {
36546 callback: function(){
36547 this.el.setLeftTop(-10000, -10000);
36549 this.afterSlideIn();
36557 slideInIf : function(e){
36558 if(!e.within(this.el)){
36563 animateCollapse : function(){
36564 this.beforeSlide();
36565 this.el.setStyle("z-index", 20000);
36566 var anchor = this.getSlideAnchor();
36567 this.el.slideOut(anchor, {
36568 callback : function(){
36569 this.el.setStyle("z-index", "");
36570 this.collapsedEl.slideIn(anchor, {duration:.3});
36572 this.el.setLocation(-10000,-10000);
36574 this.fireEvent("collapsed", this);
36581 animateExpand : function(){
36582 this.beforeSlide();
36583 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36584 this.el.setStyle("z-index", 20000);
36585 this.collapsedEl.hide({
36588 this.el.slideIn(this.getSlideAnchor(), {
36589 callback : function(){
36590 this.el.setStyle("z-index", "");
36593 this.split.el.show();
36595 this.fireEvent("invalidated", this);
36596 this.fireEvent("expanded", this);
36624 getAnchor : function(){
36625 return this.anchors[this.position];
36628 getCollapseAnchor : function(){
36629 return this.canchors[this.position];
36632 getSlideAnchor : function(){
36633 return this.sanchors[this.position];
36636 getAlignAdj : function(){
36637 var cm = this.cmargins;
36638 switch(this.position){
36654 getExpandAdj : function(){
36655 var c = this.collapsedEl, cm = this.cmargins;
36656 switch(this.position){
36658 return [-(cm.right+c.getWidth()+cm.left), 0];
36661 return [cm.right+c.getWidth()+cm.left, 0];
36664 return [0, -(cm.top+cm.bottom+c.getHeight())];
36667 return [0, cm.top+cm.bottom+c.getHeight()];
36673 * Ext JS Library 1.1.1
36674 * Copyright(c) 2006-2007, Ext JS, LLC.
36676 * Originally Released Under LGPL - original licence link has changed is not relivant.
36679 * <script type="text/javascript">
36682 * These classes are private internal classes
36684 Roo.bootstrap.layout.Center = function(config){
36685 config.region = "center";
36686 Roo.bootstrap.layout.Region.call(this, config);
36687 this.visible = true;
36688 this.minWidth = config.minWidth || 20;
36689 this.minHeight = config.minHeight || 20;
36692 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36694 // center panel can't be hidden
36698 // center panel can't be hidden
36701 getMinWidth: function(){
36702 return this.minWidth;
36705 getMinHeight: function(){
36706 return this.minHeight;
36719 Roo.bootstrap.layout.North = function(config)
36721 config.region = 'north';
36722 config.cursor = 'n-resize';
36724 Roo.bootstrap.layout.Split.call(this, config);
36728 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36729 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36730 this.split.el.addClass("roo-layout-split-v");
36732 var size = config.initialSize || config.height;
36733 if(typeof size != "undefined"){
36734 this.el.setHeight(size);
36737 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36739 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36743 getBox : function(){
36744 if(this.collapsed){
36745 return this.collapsedEl.getBox();
36747 var box = this.el.getBox();
36749 box.height += this.split.el.getHeight();
36754 updateBox : function(box){
36755 if(this.split && !this.collapsed){
36756 box.height -= this.split.el.getHeight();
36757 this.split.el.setLeft(box.x);
36758 this.split.el.setTop(box.y+box.height);
36759 this.split.el.setWidth(box.width);
36761 if(this.collapsed){
36762 this.updateBody(box.width, null);
36764 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36772 Roo.bootstrap.layout.South = function(config){
36773 config.region = 'south';
36774 config.cursor = 's-resize';
36775 Roo.bootstrap.layout.Split.call(this, config);
36777 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36778 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36779 this.split.el.addClass("roo-layout-split-v");
36781 var size = config.initialSize || config.height;
36782 if(typeof size != "undefined"){
36783 this.el.setHeight(size);
36787 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36788 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36789 getBox : function(){
36790 if(this.collapsed){
36791 return this.collapsedEl.getBox();
36793 var box = this.el.getBox();
36795 var sh = this.split.el.getHeight();
36802 updateBox : function(box){
36803 if(this.split && !this.collapsed){
36804 var sh = this.split.el.getHeight();
36807 this.split.el.setLeft(box.x);
36808 this.split.el.setTop(box.y-sh);
36809 this.split.el.setWidth(box.width);
36811 if(this.collapsed){
36812 this.updateBody(box.width, null);
36814 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36818 Roo.bootstrap.layout.East = function(config){
36819 config.region = "east";
36820 config.cursor = "e-resize";
36821 Roo.bootstrap.layout.Split.call(this, config);
36823 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36824 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36825 this.split.el.addClass("roo-layout-split-h");
36827 var size = config.initialSize || config.width;
36828 if(typeof size != "undefined"){
36829 this.el.setWidth(size);
36832 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36833 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36834 getBox : function(){
36835 if(this.collapsed){
36836 return this.collapsedEl.getBox();
36838 var box = this.el.getBox();
36840 var sw = this.split.el.getWidth();
36847 updateBox : function(box){
36848 if(this.split && !this.collapsed){
36849 var sw = this.split.el.getWidth();
36851 this.split.el.setLeft(box.x);
36852 this.split.el.setTop(box.y);
36853 this.split.el.setHeight(box.height);
36856 if(this.collapsed){
36857 this.updateBody(null, box.height);
36859 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36863 Roo.bootstrap.layout.West = function(config){
36864 config.region = "west";
36865 config.cursor = "w-resize";
36867 Roo.bootstrap.layout.Split.call(this, config);
36869 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36870 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36871 this.split.el.addClass("roo-layout-split-h");
36875 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36876 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36878 onRender: function(ctr, pos)
36880 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36881 var size = this.config.initialSize || this.config.width;
36882 if(typeof size != "undefined"){
36883 this.el.setWidth(size);
36887 getBox : function(){
36888 if(this.collapsed){
36889 return this.collapsedEl.getBox();
36891 var box = this.el.getBox();
36893 box.width += this.split.el.getWidth();
36898 updateBox : function(box){
36899 if(this.split && !this.collapsed){
36900 var sw = this.split.el.getWidth();
36902 this.split.el.setLeft(box.x+box.width);
36903 this.split.el.setTop(box.y);
36904 this.split.el.setHeight(box.height);
36906 if(this.collapsed){
36907 this.updateBody(null, box.height);
36909 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36912 Roo.namespace("Roo.bootstrap.panel");/*
36914 * Ext JS Library 1.1.1
36915 * Copyright(c) 2006-2007, Ext JS, LLC.
36917 * Originally Released Under LGPL - original licence link has changed is not relivant.
36920 * <script type="text/javascript">
36923 * @class Roo.ContentPanel
36924 * @extends Roo.util.Observable
36925 * A basic ContentPanel element.
36926 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36927 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36928 * @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
36929 * @cfg {Boolean} closable True if the panel can be closed/removed
36930 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36931 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36932 * @cfg {Toolbar} toolbar A toolbar for this panel
36933 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36934 * @cfg {String} title The title for this panel
36935 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36936 * @cfg {String} url Calls {@link #setUrl} with this value
36937 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36938 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36939 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36940 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36941 * @cfg {Boolean} badges render the badges
36944 * Create a new ContentPanel.
36945 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36946 * @param {String/Object} config A string to set only the title or a config object
36947 * @param {String} content (optional) Set the HTML content for this panel
36948 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36950 Roo.bootstrap.panel.Content = function( config){
36952 this.tpl = config.tpl || false;
36954 var el = config.el;
36955 var content = config.content;
36957 if(config.autoCreate){ // xtype is available if this is called from factory
36960 this.el = Roo.get(el);
36961 if(!this.el && config && config.autoCreate){
36962 if(typeof config.autoCreate == "object"){
36963 if(!config.autoCreate.id){
36964 config.autoCreate.id = config.id||el;
36966 this.el = Roo.DomHelper.append(document.body,
36967 config.autoCreate, true);
36969 var elcfg = { tag: "div",
36970 cls: "roo-layout-inactive-content",
36974 elcfg.html = config.html;
36978 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36981 this.closable = false;
36982 this.loaded = false;
36983 this.active = false;
36986 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36988 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36990 this.wrapEl = this.el; //this.el.wrap();
36992 if (config.toolbar.items) {
36993 ti = config.toolbar.items ;
36994 delete config.toolbar.items ;
36998 this.toolbar.render(this.wrapEl, 'before');
36999 for(var i =0;i < ti.length;i++) {
37000 // Roo.log(['add child', items[i]]);
37001 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37003 this.toolbar.items = nitems;
37004 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37005 delete config.toolbar;
37009 // xtype created footer. - not sure if will work as we normally have to render first..
37010 if (this.footer && !this.footer.el && this.footer.xtype) {
37011 if (!this.wrapEl) {
37012 this.wrapEl = this.el.wrap();
37015 this.footer.container = this.wrapEl.createChild();
37017 this.footer = Roo.factory(this.footer, Roo);
37022 if(typeof config == "string"){
37023 this.title = config;
37025 Roo.apply(this, config);
37029 this.resizeEl = Roo.get(this.resizeEl, true);
37031 this.resizeEl = this.el;
37033 // handle view.xtype
37041 * Fires when this panel is activated.
37042 * @param {Roo.ContentPanel} this
37046 * @event deactivate
37047 * Fires when this panel is activated.
37048 * @param {Roo.ContentPanel} this
37050 "deactivate" : true,
37054 * Fires when this panel is resized if fitToFrame is true.
37055 * @param {Roo.ContentPanel} this
37056 * @param {Number} width The width after any component adjustments
37057 * @param {Number} height The height after any component adjustments
37063 * Fires when this tab is created
37064 * @param {Roo.ContentPanel} this
37075 if(this.autoScroll){
37076 this.resizeEl.setStyle("overflow", "auto");
37078 // fix randome scrolling
37079 //this.el.on('scroll', function() {
37080 // Roo.log('fix random scolling');
37081 // this.scrollTo('top',0);
37084 content = content || this.content;
37086 this.setContent(content);
37088 if(config && config.url){
37089 this.setUrl(this.url, this.params, this.loadOnce);
37094 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37096 if (this.view && typeof(this.view.xtype) != 'undefined') {
37097 this.view.el = this.el.appendChild(document.createElement("div"));
37098 this.view = Roo.factory(this.view);
37099 this.view.render && this.view.render(false, '');
37103 this.fireEvent('render', this);
37106 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37110 setRegion : function(region){
37111 this.region = region;
37112 this.setActiveClass(region && !this.background);
37116 setActiveClass: function(state)
37119 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37120 this.el.setStyle('position','relative');
37122 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37123 this.el.setStyle('position', 'absolute');
37128 * Returns the toolbar for this Panel if one was configured.
37129 * @return {Roo.Toolbar}
37131 getToolbar : function(){
37132 return this.toolbar;
37135 setActiveState : function(active)
37137 this.active = active;
37138 this.setActiveClass(active);
37140 if(this.fireEvent("deactivate", this) === false){
37145 this.fireEvent("activate", this);
37149 * Updates this panel's element
37150 * @param {String} content The new content
37151 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37153 setContent : function(content, loadScripts){
37154 this.el.update(content, loadScripts);
37157 ignoreResize : function(w, h){
37158 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37161 this.lastSize = {width: w, height: h};
37166 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37167 * @return {Roo.UpdateManager} The UpdateManager
37169 getUpdateManager : function(){
37170 return this.el.getUpdateManager();
37173 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37174 * @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:
37177 url: "your-url.php",
37178 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37179 callback: yourFunction,
37180 scope: yourObject, //(optional scope)
37183 text: "Loading...",
37188 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37189 * 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.
37190 * @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}
37191 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37192 * @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.
37193 * @return {Roo.ContentPanel} this
37196 var um = this.el.getUpdateManager();
37197 um.update.apply(um, arguments);
37203 * 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.
37204 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37205 * @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)
37206 * @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)
37207 * @return {Roo.UpdateManager} The UpdateManager
37209 setUrl : function(url, params, loadOnce){
37210 if(this.refreshDelegate){
37211 this.removeListener("activate", this.refreshDelegate);
37213 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37214 this.on("activate", this.refreshDelegate);
37215 return this.el.getUpdateManager();
37218 _handleRefresh : function(url, params, loadOnce){
37219 if(!loadOnce || !this.loaded){
37220 var updater = this.el.getUpdateManager();
37221 updater.update(url, params, this._setLoaded.createDelegate(this));
37225 _setLoaded : function(){
37226 this.loaded = true;
37230 * Returns this panel's id
37233 getId : function(){
37238 * Returns this panel's element - used by regiosn to add.
37239 * @return {Roo.Element}
37241 getEl : function(){
37242 return this.wrapEl || this.el;
37247 adjustForComponents : function(width, height)
37249 //Roo.log('adjustForComponents ');
37250 if(this.resizeEl != this.el){
37251 width -= this.el.getFrameWidth('lr');
37252 height -= this.el.getFrameWidth('tb');
37255 var te = this.toolbar.getEl();
37256 te.setWidth(width);
37257 height -= te.getHeight();
37260 var te = this.footer.getEl();
37261 te.setWidth(width);
37262 height -= te.getHeight();
37266 if(this.adjustments){
37267 width += this.adjustments[0];
37268 height += this.adjustments[1];
37270 return {"width": width, "height": height};
37273 setSize : function(width, height){
37274 if(this.fitToFrame && !this.ignoreResize(width, height)){
37275 if(this.fitContainer && this.resizeEl != this.el){
37276 this.el.setSize(width, height);
37278 var size = this.adjustForComponents(width, height);
37279 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37280 this.fireEvent('resize', this, size.width, size.height);
37285 * Returns this panel's title
37288 getTitle : function(){
37290 if (typeof(this.title) != 'object') {
37295 for (var k in this.title) {
37296 if (!this.title.hasOwnProperty(k)) {
37300 if (k.indexOf('-') >= 0) {
37301 var s = k.split('-');
37302 for (var i = 0; i<s.length; i++) {
37303 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37306 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37313 * Set this panel's title
37314 * @param {String} title
37316 setTitle : function(title){
37317 this.title = title;
37319 this.region.updatePanelTitle(this, title);
37324 * Returns true is this panel was configured to be closable
37325 * @return {Boolean}
37327 isClosable : function(){
37328 return this.closable;
37331 beforeSlide : function(){
37333 this.resizeEl.clip();
37336 afterSlide : function(){
37338 this.resizeEl.unclip();
37342 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37343 * Will fail silently if the {@link #setUrl} method has not been called.
37344 * This does not activate the panel, just updates its content.
37346 refresh : function(){
37347 if(this.refreshDelegate){
37348 this.loaded = false;
37349 this.refreshDelegate();
37354 * Destroys this panel
37356 destroy : function(){
37357 this.el.removeAllListeners();
37358 var tempEl = document.createElement("span");
37359 tempEl.appendChild(this.el.dom);
37360 tempEl.innerHTML = "";
37366 * form - if the content panel contains a form - this is a reference to it.
37367 * @type {Roo.form.Form}
37371 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37372 * This contains a reference to it.
37378 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37388 * @param {Object} cfg Xtype definition of item to add.
37392 getChildContainer: function () {
37393 return this.getEl();
37398 var ret = new Roo.factory(cfg);
37403 if (cfg.xtype.match(/^Form$/)) {
37406 //if (this.footer) {
37407 // el = this.footer.container.insertSibling(false, 'before');
37409 el = this.el.createChild();
37412 this.form = new Roo.form.Form(cfg);
37415 if ( this.form.allItems.length) {
37416 this.form.render(el.dom);
37420 // should only have one of theses..
37421 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37422 // views.. should not be just added - used named prop 'view''
37424 cfg.el = this.el.appendChild(document.createElement("div"));
37427 var ret = new Roo.factory(cfg);
37429 ret.render && ret.render(false, ''); // render blank..
37439 * @class Roo.bootstrap.panel.Grid
37440 * @extends Roo.bootstrap.panel.Content
37442 * Create a new GridPanel.
37443 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37444 * @param {Object} config A the config object
37450 Roo.bootstrap.panel.Grid = function(config)
37454 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37455 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37457 config.el = this.wrapper;
37458 //this.el = this.wrapper;
37460 if (config.container) {
37461 // ctor'ed from a Border/panel.grid
37464 this.wrapper.setStyle("overflow", "hidden");
37465 this.wrapper.addClass('roo-grid-container');
37470 if(config.toolbar){
37471 var tool_el = this.wrapper.createChild();
37472 this.toolbar = Roo.factory(config.toolbar);
37474 if (config.toolbar.items) {
37475 ti = config.toolbar.items ;
37476 delete config.toolbar.items ;
37480 this.toolbar.render(tool_el);
37481 for(var i =0;i < ti.length;i++) {
37482 // Roo.log(['add child', items[i]]);
37483 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37485 this.toolbar.items = nitems;
37487 delete config.toolbar;
37490 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37491 config.grid.scrollBody = true;;
37492 config.grid.monitorWindowResize = false; // turn off autosizing
37493 config.grid.autoHeight = false;
37494 config.grid.autoWidth = false;
37496 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37498 if (config.background) {
37499 // render grid on panel activation (if panel background)
37500 this.on('activate', function(gp) {
37501 if (!gp.grid.rendered) {
37502 gp.grid.render(this.wrapper);
37503 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37508 this.grid.render(this.wrapper);
37509 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37512 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37513 // ??? needed ??? config.el = this.wrapper;
37518 // xtype created footer. - not sure if will work as we normally have to render first..
37519 if (this.footer && !this.footer.el && this.footer.xtype) {
37521 var ctr = this.grid.getView().getFooterPanel(true);
37522 this.footer.dataSource = this.grid.dataSource;
37523 this.footer = Roo.factory(this.footer, Roo);
37524 this.footer.render(ctr);
37534 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37535 getId : function(){
37536 return this.grid.id;
37540 * Returns the grid for this panel
37541 * @return {Roo.bootstrap.Table}
37543 getGrid : function(){
37547 setSize : function(width, height){
37548 if(!this.ignoreResize(width, height)){
37549 var grid = this.grid;
37550 var size = this.adjustForComponents(width, height);
37551 var gridel = grid.getGridEl();
37552 gridel.setSize(size.width, size.height);
37554 var thd = grid.getGridEl().select('thead',true).first();
37555 var tbd = grid.getGridEl().select('tbody', true).first();
37557 tbd.setSize(width, height - thd.getHeight());
37566 beforeSlide : function(){
37567 this.grid.getView().scroller.clip();
37570 afterSlide : function(){
37571 this.grid.getView().scroller.unclip();
37574 destroy : function(){
37575 this.grid.destroy();
37577 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37582 * @class Roo.bootstrap.panel.Nest
37583 * @extends Roo.bootstrap.panel.Content
37585 * Create a new Panel, that can contain a layout.Border.
37588 * @param {Roo.BorderLayout} layout The layout for this panel
37589 * @param {String/Object} config A string to set only the title or a config object
37591 Roo.bootstrap.panel.Nest = function(config)
37593 // construct with only one argument..
37594 /* FIXME - implement nicer consturctors
37595 if (layout.layout) {
37597 layout = config.layout;
37598 delete config.layout;
37600 if (layout.xtype && !layout.getEl) {
37601 // then layout needs constructing..
37602 layout = Roo.factory(layout, Roo);
37606 config.el = config.layout.getEl();
37608 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37610 config.layout.monitorWindowResize = false; // turn off autosizing
37611 this.layout = config.layout;
37612 this.layout.getEl().addClass("roo-layout-nested-layout");
37619 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37621 setSize : function(width, height){
37622 if(!this.ignoreResize(width, height)){
37623 var size = this.adjustForComponents(width, height);
37624 var el = this.layout.getEl();
37625 if (size.height < 1) {
37626 el.setWidth(size.width);
37628 el.setSize(size.width, size.height);
37630 var touch = el.dom.offsetWidth;
37631 this.layout.layout();
37632 // ie requires a double layout on the first pass
37633 if(Roo.isIE && !this.initialized){
37634 this.initialized = true;
37635 this.layout.layout();
37640 // activate all subpanels if not currently active..
37642 setActiveState : function(active){
37643 this.active = active;
37644 this.setActiveClass(active);
37647 this.fireEvent("deactivate", this);
37651 this.fireEvent("activate", this);
37652 // not sure if this should happen before or after..
37653 if (!this.layout) {
37654 return; // should not happen..
37657 for (var r in this.layout.regions) {
37658 reg = this.layout.getRegion(r);
37659 if (reg.getActivePanel()) {
37660 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37661 reg.setActivePanel(reg.getActivePanel());
37664 if (!reg.panels.length) {
37667 reg.showPanel(reg.getPanel(0));
37676 * Returns the nested BorderLayout for this panel
37677 * @return {Roo.BorderLayout}
37679 getLayout : function(){
37680 return this.layout;
37684 * Adds a xtype elements to the layout of the nested panel
37688 xtype : 'ContentPanel',
37695 xtype : 'NestedLayoutPanel',
37701 items : [ ... list of content panels or nested layout panels.. ]
37705 * @param {Object} cfg Xtype definition of item to add.
37707 addxtype : function(cfg) {
37708 return this.layout.addxtype(cfg);
37713 * Ext JS Library 1.1.1
37714 * Copyright(c) 2006-2007, Ext JS, LLC.
37716 * Originally Released Under LGPL - original licence link has changed is not relivant.
37719 * <script type="text/javascript">
37722 * @class Roo.TabPanel
37723 * @extends Roo.util.Observable
37724 * A lightweight tab container.
37728 // basic tabs 1, built from existing content
37729 var tabs = new Roo.TabPanel("tabs1");
37730 tabs.addTab("script", "View Script");
37731 tabs.addTab("markup", "View Markup");
37732 tabs.activate("script");
37734 // more advanced tabs, built from javascript
37735 var jtabs = new Roo.TabPanel("jtabs");
37736 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37738 // set up the UpdateManager
37739 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37740 var updater = tab2.getUpdateManager();
37741 updater.setDefaultUrl("ajax1.htm");
37742 tab2.on('activate', updater.refresh, updater, true);
37744 // Use setUrl for Ajax loading
37745 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37746 tab3.setUrl("ajax2.htm", null, true);
37749 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37752 jtabs.activate("jtabs-1");
37755 * Create a new TabPanel.
37756 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37757 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37759 Roo.bootstrap.panel.Tabs = function(config){
37761 * The container element for this TabPanel.
37762 * @type Roo.Element
37764 this.el = Roo.get(config.el);
37767 if(typeof config == "boolean"){
37768 this.tabPosition = config ? "bottom" : "top";
37770 Roo.apply(this, config);
37774 if(this.tabPosition == "bottom"){
37775 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37776 this.el.addClass("roo-tabs-bottom");
37778 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37779 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37780 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37782 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37784 if(this.tabPosition != "bottom"){
37785 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37786 * @type Roo.Element
37788 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37789 this.el.addClass("roo-tabs-top");
37793 this.bodyEl.setStyle("position", "relative");
37795 this.active = null;
37796 this.activateDelegate = this.activate.createDelegate(this);
37801 * Fires when the active tab changes
37802 * @param {Roo.TabPanel} this
37803 * @param {Roo.TabPanelItem} activePanel The new active tab
37807 * @event beforetabchange
37808 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37809 * @param {Roo.TabPanel} this
37810 * @param {Object} e Set cancel to true on this object to cancel the tab change
37811 * @param {Roo.TabPanelItem} tab The tab being changed to
37813 "beforetabchange" : true
37816 Roo.EventManager.onWindowResize(this.onResize, this);
37817 this.cpad = this.el.getPadding("lr");
37818 this.hiddenCount = 0;
37821 // toolbar on the tabbar support...
37822 if (this.toolbar) {
37823 alert("no toolbar support yet");
37824 this.toolbar = false;
37826 var tcfg = this.toolbar;
37827 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37828 this.toolbar = new Roo.Toolbar(tcfg);
37829 if (Roo.isSafari) {
37830 var tbl = tcfg.container.child('table', true);
37831 tbl.setAttribute('width', '100%');
37839 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37842 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37844 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37846 tabPosition : "top",
37848 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37850 currentTabWidth : 0,
37852 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37856 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37860 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37862 preferredTabWidth : 175,
37864 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37866 resizeTabs : false,
37868 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37870 monitorResize : true,
37872 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37877 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37878 * @param {String} id The id of the div to use <b>or create</b>
37879 * @param {String} text The text for the tab
37880 * @param {String} content (optional) Content to put in the TabPanelItem body
37881 * @param {Boolean} closable (optional) True to create a close icon on the tab
37882 * @return {Roo.TabPanelItem} The created TabPanelItem
37884 addTab : function(id, text, content, closable, tpl)
37886 var item = new Roo.bootstrap.panel.TabItem({
37890 closable : closable,
37893 this.addTabItem(item);
37895 item.setContent(content);
37901 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37902 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37903 * @return {Roo.TabPanelItem}
37905 getTab : function(id){
37906 return this.items[id];
37910 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37911 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37913 hideTab : function(id){
37914 var t = this.items[id];
37917 this.hiddenCount++;
37918 this.autoSizeTabs();
37923 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37924 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37926 unhideTab : function(id){
37927 var t = this.items[id];
37929 t.setHidden(false);
37930 this.hiddenCount--;
37931 this.autoSizeTabs();
37936 * Adds an existing {@link Roo.TabPanelItem}.
37937 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37939 addTabItem : function(item){
37940 this.items[item.id] = item;
37941 this.items.push(item);
37942 // if(this.resizeTabs){
37943 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37944 // this.autoSizeTabs();
37946 // item.autoSize();
37951 * Removes a {@link Roo.TabPanelItem}.
37952 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37954 removeTab : function(id){
37955 var items = this.items;
37956 var tab = items[id];
37957 if(!tab) { return; }
37958 var index = items.indexOf(tab);
37959 if(this.active == tab && items.length > 1){
37960 var newTab = this.getNextAvailable(index);
37965 this.stripEl.dom.removeChild(tab.pnode.dom);
37966 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37967 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37969 items.splice(index, 1);
37970 delete this.items[tab.id];
37971 tab.fireEvent("close", tab);
37972 tab.purgeListeners();
37973 this.autoSizeTabs();
37976 getNextAvailable : function(start){
37977 var items = this.items;
37979 // look for a next tab that will slide over to
37980 // replace the one being removed
37981 while(index < items.length){
37982 var item = items[++index];
37983 if(item && !item.isHidden()){
37987 // if one isn't found select the previous tab (on the left)
37990 var item = items[--index];
37991 if(item && !item.isHidden()){
37999 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38000 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38002 disableTab : function(id){
38003 var tab = this.items[id];
38004 if(tab && this.active != tab){
38010 * Enables a {@link Roo.TabPanelItem} that is disabled.
38011 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38013 enableTab : function(id){
38014 var tab = this.items[id];
38019 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38020 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38021 * @return {Roo.TabPanelItem} The TabPanelItem.
38023 activate : function(id){
38024 var tab = this.items[id];
38028 if(tab == this.active || tab.disabled){
38032 this.fireEvent("beforetabchange", this, e, tab);
38033 if(e.cancel !== true && !tab.disabled){
38035 this.active.hide();
38037 this.active = this.items[id];
38038 this.active.show();
38039 this.fireEvent("tabchange", this, this.active);
38045 * Gets the active {@link Roo.TabPanelItem}.
38046 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38048 getActiveTab : function(){
38049 return this.active;
38053 * Updates the tab body element to fit the height of the container element
38054 * for overflow scrolling
38055 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38057 syncHeight : function(targetHeight){
38058 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38059 var bm = this.bodyEl.getMargins();
38060 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38061 this.bodyEl.setHeight(newHeight);
38065 onResize : function(){
38066 if(this.monitorResize){
38067 this.autoSizeTabs();
38072 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38074 beginUpdate : function(){
38075 this.updating = true;
38079 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38081 endUpdate : function(){
38082 this.updating = false;
38083 this.autoSizeTabs();
38087 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38089 autoSizeTabs : function(){
38090 var count = this.items.length;
38091 var vcount = count - this.hiddenCount;
38092 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38095 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38096 var availWidth = Math.floor(w / vcount);
38097 var b = this.stripBody;
38098 if(b.getWidth() > w){
38099 var tabs = this.items;
38100 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38101 if(availWidth < this.minTabWidth){
38102 /*if(!this.sleft){ // incomplete scrolling code
38103 this.createScrollButtons();
38106 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38109 if(this.currentTabWidth < this.preferredTabWidth){
38110 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38116 * Returns the number of tabs in this TabPanel.
38119 getCount : function(){
38120 return this.items.length;
38124 * Resizes all the tabs to the passed width
38125 * @param {Number} The new width
38127 setTabWidth : function(width){
38128 this.currentTabWidth = width;
38129 for(var i = 0, len = this.items.length; i < len; i++) {
38130 if(!this.items[i].isHidden()) {
38131 this.items[i].setWidth(width);
38137 * Destroys this TabPanel
38138 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38140 destroy : function(removeEl){
38141 Roo.EventManager.removeResizeListener(this.onResize, this);
38142 for(var i = 0, len = this.items.length; i < len; i++){
38143 this.items[i].purgeListeners();
38145 if(removeEl === true){
38146 this.el.update("");
38151 createStrip : function(container)
38153 var strip = document.createElement("nav");
38154 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38155 container.appendChild(strip);
38159 createStripList : function(strip)
38161 // div wrapper for retard IE
38162 // returns the "tr" element.
38163 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38164 //'<div class="x-tabs-strip-wrap">'+
38165 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38166 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38167 return strip.firstChild; //.firstChild.firstChild.firstChild;
38169 createBody : function(container)
38171 var body = document.createElement("div");
38172 Roo.id(body, "tab-body");
38173 //Roo.fly(body).addClass("x-tabs-body");
38174 Roo.fly(body).addClass("tab-content");
38175 container.appendChild(body);
38178 createItemBody :function(bodyEl, id){
38179 var body = Roo.getDom(id);
38181 body = document.createElement("div");
38184 //Roo.fly(body).addClass("x-tabs-item-body");
38185 Roo.fly(body).addClass("tab-pane");
38186 bodyEl.insertBefore(body, bodyEl.firstChild);
38190 createStripElements : function(stripEl, text, closable, tpl)
38192 var td = document.createElement("li"); // was td..
38195 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38198 stripEl.appendChild(td);
38200 td.className = "x-tabs-closable";
38201 if(!this.closeTpl){
38202 this.closeTpl = new Roo.Template(
38203 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38204 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38205 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38208 var el = this.closeTpl.overwrite(td, {"text": text});
38209 var close = el.getElementsByTagName("div")[0];
38210 var inner = el.getElementsByTagName("em")[0];
38211 return {"el": el, "close": close, "inner": inner};
38214 // not sure what this is..
38215 // if(!this.tabTpl){
38216 //this.tabTpl = new Roo.Template(
38217 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38218 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38220 // this.tabTpl = new Roo.Template(
38221 // '<a href="#">' +
38222 // '<span unselectable="on"' +
38223 // (this.disableTooltips ? '' : ' title="{text}"') +
38224 // ' >{text}</span></a>'
38230 var template = tpl || this.tabTpl || false;
38234 template = new Roo.Template(
38236 '<span unselectable="on"' +
38237 (this.disableTooltips ? '' : ' title="{text}"') +
38238 ' >{text}</span></a>'
38242 switch (typeof(template)) {
38246 template = new Roo.Template(template);
38252 var el = template.overwrite(td, {"text": text});
38254 var inner = el.getElementsByTagName("span")[0];
38256 return {"el": el, "inner": inner};
38264 * @class Roo.TabPanelItem
38265 * @extends Roo.util.Observable
38266 * Represents an individual item (tab plus body) in a TabPanel.
38267 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38268 * @param {String} id The id of this TabPanelItem
38269 * @param {String} text The text for the tab of this TabPanelItem
38270 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38272 Roo.bootstrap.panel.TabItem = function(config){
38274 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38275 * @type Roo.TabPanel
38277 this.tabPanel = config.panel;
38279 * The id for this TabPanelItem
38282 this.id = config.id;
38284 this.disabled = false;
38286 this.text = config.text;
38288 this.loaded = false;
38289 this.closable = config.closable;
38292 * The body element for this TabPanelItem.
38293 * @type Roo.Element
38295 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38296 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38297 this.bodyEl.setStyle("display", "block");
38298 this.bodyEl.setStyle("zoom", "1");
38299 //this.hideAction();
38301 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38303 this.el = Roo.get(els.el);
38304 this.inner = Roo.get(els.inner, true);
38305 this.textEl = Roo.get(this.el.dom.firstChild, true);
38306 this.pnode = Roo.get(els.el.parentNode, true);
38307 // this.el.on("mousedown", this.onTabMouseDown, this);
38308 this.el.on("click", this.onTabClick, this);
38310 if(config.closable){
38311 var c = Roo.get(els.close, true);
38312 c.dom.title = this.closeText;
38313 c.addClassOnOver("close-over");
38314 c.on("click", this.closeClick, this);
38320 * Fires when this tab becomes the active tab.
38321 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38322 * @param {Roo.TabPanelItem} this
38326 * @event beforeclose
38327 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38328 * @param {Roo.TabPanelItem} this
38329 * @param {Object} e Set cancel to true on this object to cancel the close.
38331 "beforeclose": true,
38334 * Fires when this tab is closed.
38335 * @param {Roo.TabPanelItem} this
38339 * @event deactivate
38340 * Fires when this tab is no longer the active tab.
38341 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38342 * @param {Roo.TabPanelItem} this
38344 "deactivate" : true
38346 this.hidden = false;
38348 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38351 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38353 purgeListeners : function(){
38354 Roo.util.Observable.prototype.purgeListeners.call(this);
38355 this.el.removeAllListeners();
38358 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38361 this.pnode.addClass("active");
38364 this.tabPanel.stripWrap.repaint();
38366 this.fireEvent("activate", this.tabPanel, this);
38370 * Returns true if this tab is the active tab.
38371 * @return {Boolean}
38373 isActive : function(){
38374 return this.tabPanel.getActiveTab() == this;
38378 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38381 this.pnode.removeClass("active");
38383 this.fireEvent("deactivate", this.tabPanel, this);
38386 hideAction : function(){
38387 this.bodyEl.hide();
38388 this.bodyEl.setStyle("position", "absolute");
38389 this.bodyEl.setLeft("-20000px");
38390 this.bodyEl.setTop("-20000px");
38393 showAction : function(){
38394 this.bodyEl.setStyle("position", "relative");
38395 this.bodyEl.setTop("");
38396 this.bodyEl.setLeft("");
38397 this.bodyEl.show();
38401 * Set the tooltip for the tab.
38402 * @param {String} tooltip The tab's tooltip
38404 setTooltip : function(text){
38405 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38406 this.textEl.dom.qtip = text;
38407 this.textEl.dom.removeAttribute('title');
38409 this.textEl.dom.title = text;
38413 onTabClick : function(e){
38414 e.preventDefault();
38415 this.tabPanel.activate(this.id);
38418 onTabMouseDown : function(e){
38419 e.preventDefault();
38420 this.tabPanel.activate(this.id);
38423 getWidth : function(){
38424 return this.inner.getWidth();
38427 setWidth : function(width){
38428 var iwidth = width - this.pnode.getPadding("lr");
38429 this.inner.setWidth(iwidth);
38430 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38431 this.pnode.setWidth(width);
38435 * Show or hide the tab
38436 * @param {Boolean} hidden True to hide or false to show.
38438 setHidden : function(hidden){
38439 this.hidden = hidden;
38440 this.pnode.setStyle("display", hidden ? "none" : "");
38444 * Returns true if this tab is "hidden"
38445 * @return {Boolean}
38447 isHidden : function(){
38448 return this.hidden;
38452 * Returns the text for this tab
38455 getText : function(){
38459 autoSize : function(){
38460 //this.el.beginMeasure();
38461 this.textEl.setWidth(1);
38463 * #2804 [new] Tabs in Roojs
38464 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38466 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38467 //this.el.endMeasure();
38471 * Sets the text for the tab (Note: this also sets the tooltip text)
38472 * @param {String} text The tab's text and tooltip
38474 setText : function(text){
38476 this.textEl.update(text);
38477 this.setTooltip(text);
38478 //if(!this.tabPanel.resizeTabs){
38479 // this.autoSize();
38483 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38485 activate : function(){
38486 this.tabPanel.activate(this.id);
38490 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38492 disable : function(){
38493 if(this.tabPanel.active != this){
38494 this.disabled = true;
38495 this.pnode.addClass("disabled");
38500 * Enables this TabPanelItem if it was previously disabled.
38502 enable : function(){
38503 this.disabled = false;
38504 this.pnode.removeClass("disabled");
38508 * Sets the content for this TabPanelItem.
38509 * @param {String} content The content
38510 * @param {Boolean} loadScripts true to look for and load scripts
38512 setContent : function(content, loadScripts){
38513 this.bodyEl.update(content, loadScripts);
38517 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38518 * @return {Roo.UpdateManager} The UpdateManager
38520 getUpdateManager : function(){
38521 return this.bodyEl.getUpdateManager();
38525 * Set a URL to be used to load the content for this TabPanelItem.
38526 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38527 * @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)
38528 * @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)
38529 * @return {Roo.UpdateManager} The UpdateManager
38531 setUrl : function(url, params, loadOnce){
38532 if(this.refreshDelegate){
38533 this.un('activate', this.refreshDelegate);
38535 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38536 this.on("activate", this.refreshDelegate);
38537 return this.bodyEl.getUpdateManager();
38541 _handleRefresh : function(url, params, loadOnce){
38542 if(!loadOnce || !this.loaded){
38543 var updater = this.bodyEl.getUpdateManager();
38544 updater.update(url, params, this._setLoaded.createDelegate(this));
38549 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38550 * Will fail silently if the setUrl method has not been called.
38551 * This does not activate the panel, just updates its content.
38553 refresh : function(){
38554 if(this.refreshDelegate){
38555 this.loaded = false;
38556 this.refreshDelegate();
38561 _setLoaded : function(){
38562 this.loaded = true;
38566 closeClick : function(e){
38569 this.fireEvent("beforeclose", this, o);
38570 if(o.cancel !== true){
38571 this.tabPanel.removeTab(this.id);
38575 * The text displayed in the tooltip for the close icon.
38578 closeText : "Close this tab"
38581 * This script refer to:
38582 * Title: International Telephone Input
38583 * Author: Jack O'Connor
38584 * Code version: v12.1.12
38585 * Availability: https://github.com/jackocnr/intl-tel-input.git
38588 Roo.bootstrap.PhoneInputData = function() {
38591 "Afghanistan (افغانستان)",
38596 "Albania (Shqipëri)",
38601 "Algeria (الجزائر)",
38626 "Antigua and Barbuda",
38636 "Armenia (Հայաստան)",
38652 "Austria (Österreich)",
38657 "Azerbaijan (Azərbaycan)",
38667 "Bahrain (البحرين)",
38672 "Bangladesh (বাংলাদেশ)",
38682 "Belarus (Беларусь)",
38687 "Belgium (België)",
38717 "Bosnia and Herzegovina (Босна и Херцеговина)",
38732 "British Indian Ocean Territory",
38737 "British Virgin Islands",
38747 "Bulgaria (България)",
38757 "Burundi (Uburundi)",
38762 "Cambodia (កម្ពុជា)",
38767 "Cameroon (Cameroun)",
38776 ["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"]
38779 "Cape Verde (Kabu Verdi)",
38784 "Caribbean Netherlands",
38795 "Central African Republic (République centrafricaine)",
38815 "Christmas Island",
38821 "Cocos (Keeling) Islands",
38832 "Comoros (جزر القمر)",
38837 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38842 "Congo (Republic) (Congo-Brazzaville)",
38862 "Croatia (Hrvatska)",
38883 "Czech Republic (Česká republika)",
38888 "Denmark (Danmark)",
38903 "Dominican Republic (República Dominicana)",
38907 ["809", "829", "849"]
38925 "Equatorial Guinea (Guinea Ecuatorial)",
38945 "Falkland Islands (Islas Malvinas)",
38950 "Faroe Islands (Føroyar)",
38971 "French Guiana (Guyane française)",
38976 "French Polynesia (Polynésie française)",
38991 "Georgia (საქართველო)",
38996 "Germany (Deutschland)",
39016 "Greenland (Kalaallit Nunaat)",
39053 "Guinea-Bissau (Guiné Bissau)",
39078 "Hungary (Magyarország)",
39083 "Iceland (Ísland)",
39103 "Iraq (العراق)",
39119 "Israel (ישראל)",
39146 "Jordan (الأردن)",
39151 "Kazakhstan (Казахстан)",
39172 "Kuwait (الكويت)",
39177 "Kyrgyzstan (Кыргызстан)",
39187 "Latvia (Latvija)",
39192 "Lebanon (لبنان)",
39207 "Libya (ليبيا)",
39217 "Lithuania (Lietuva)",
39232 "Macedonia (FYROM) (Македонија)",
39237 "Madagascar (Madagasikara)",
39267 "Marshall Islands",
39277 "Mauritania (موريتانيا)",
39282 "Mauritius (Moris)",
39303 "Moldova (Republica Moldova)",
39313 "Mongolia (Монгол)",
39318 "Montenegro (Crna Gora)",
39328 "Morocco (المغرب)",
39334 "Mozambique (Moçambique)",
39339 "Myanmar (Burma) (မြန်မာ)",
39344 "Namibia (Namibië)",
39359 "Netherlands (Nederland)",
39364 "New Caledonia (Nouvelle-Calédonie)",
39399 "North Korea (조선 민주주의 인민 공화국)",
39404 "Northern Mariana Islands",
39420 "Pakistan (پاکستان)",
39430 "Palestine (فلسطين)",
39440 "Papua New Guinea",
39482 "Réunion (La Réunion)",
39488 "Romania (România)",
39504 "Saint Barthélemy",
39515 "Saint Kitts and Nevis",
39525 "Saint Martin (Saint-Martin (partie française))",
39531 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39536 "Saint Vincent and the Grenadines",
39551 "São Tomé and Príncipe (São Tomé e Príncipe)",
39556 "Saudi Arabia (المملكة العربية السعودية)",
39561 "Senegal (Sénégal)",
39591 "Slovakia (Slovensko)",
39596 "Slovenia (Slovenija)",
39606 "Somalia (Soomaaliya)",
39616 "South Korea (대한민국)",
39621 "South Sudan (جنوب السودان)",
39631 "Sri Lanka (ශ්රී ලංකාව)",
39636 "Sudan (السودان)",
39646 "Svalbard and Jan Mayen",
39657 "Sweden (Sverige)",
39662 "Switzerland (Schweiz)",
39667 "Syria (سوريا)",
39712 "Trinidad and Tobago",
39717 "Tunisia (تونس)",
39722 "Turkey (Türkiye)",
39732 "Turks and Caicos Islands",
39742 "U.S. Virgin Islands",
39752 "Ukraine (Україна)",
39757 "United Arab Emirates (الإمارات العربية المتحدة)",
39779 "Uzbekistan (Oʻzbekiston)",
39789 "Vatican City (Città del Vaticano)",
39800 "Vietnam (Việt Nam)",
39805 "Wallis and Futuna (Wallis-et-Futuna)",
39810 "Western Sahara (الصحراء الغربية)",
39816 "Yemen (اليمن)",
39840 * This script refer to:
39841 * Title: International Telephone Input
39842 * Author: Jack O'Connor
39843 * Code version: v12.1.12
39844 * Availability: https://github.com/jackocnr/intl-tel-input.git
39848 * @class Roo.bootstrap.PhoneInput
39849 * @extends Roo.bootstrap.TriggerField
39850 * An input with International dial-code selection
39852 * @cfg {String} defaultDialCode default '+852'
39853 * @cfg {Array} preferedCountries default []
39856 * Create a new PhoneInput.
39857 * @param {Object} config Configuration options
39860 Roo.bootstrap.PhoneInput = function(config) {
39861 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39864 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39866 listWidth: undefined,
39868 selectedClass: 'active',
39870 invalidClass : "has-warning",
39872 validClass: 'has-success',
39874 allowed: '0123456789',
39877 * @cfg {String} defaultDialCode The default dial code when initializing the input
39879 defaultDialCode: '+852',
39882 * @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
39884 preferedCountries: false,
39886 getAutoCreate : function()
39888 var data = Roo.bootstrap.PhoneInputData();
39889 var align = this.labelAlign || this.parentLabelAlign();
39892 this.allCountries = [];
39893 this.dialCodeMapping = [];
39895 for (var i = 0; i < data.length; i++) {
39897 this.allCountries[i] = {
39901 priority: c[3] || 0,
39902 areaCodes: c[4] || null
39904 this.dialCodeMapping[c[2]] = {
39907 priority: c[3] || 0,
39908 areaCodes: c[4] || null
39920 cls : 'form-control tel-input',
39921 autocomplete: 'new-password'
39924 var hiddenInput = {
39927 cls: 'hidden-tel-input'
39931 hiddenInput.name = this.name;
39934 if (this.disabled) {
39935 input.disabled = true;
39938 var flag_container = {
39955 cls: this.hasFeedback ? 'has-feedback' : '',
39961 cls: 'dial-code-holder',
39968 cls: 'roo-select2-container input-group',
39975 if (this.fieldLabel.length) {
39978 tooltip: 'This field is required'
39984 cls: 'control-label',
39990 html: this.fieldLabel
39993 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39999 if(this.indicatorpos == 'right') {
40000 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40007 if(align == 'left') {
40015 if(this.labelWidth > 12){
40016 label.style = "width: " + this.labelWidth + 'px';
40018 if(this.labelWidth < 13 && this.labelmd == 0){
40019 this.labelmd = this.labelWidth;
40021 if(this.labellg > 0){
40022 label.cls += ' col-lg-' + this.labellg;
40023 input.cls += ' col-lg-' + (12 - this.labellg);
40025 if(this.labelmd > 0){
40026 label.cls += ' col-md-' + this.labelmd;
40027 container.cls += ' col-md-' + (12 - this.labelmd);
40029 if(this.labelsm > 0){
40030 label.cls += ' col-sm-' + this.labelsm;
40031 container.cls += ' col-sm-' + (12 - this.labelsm);
40033 if(this.labelxs > 0){
40034 label.cls += ' col-xs-' + this.labelxs;
40035 container.cls += ' col-xs-' + (12 - this.labelxs);
40045 var settings = this;
40047 ['xs','sm','md','lg'].map(function(size){
40048 if (settings[size]) {
40049 cfg.cls += ' col-' + size + '-' + settings[size];
40053 this.store = new Roo.data.Store({
40054 proxy : new Roo.data.MemoryProxy({}),
40055 reader : new Roo.data.JsonReader({
40066 'name' : 'dialCode',
40070 'name' : 'priority',
40074 'name' : 'areaCodes',
40081 if(!this.preferedCountries) {
40082 this.preferedCountries = [
40089 var p = this.preferedCountries.reverse();
40092 for (var i = 0; i < p.length; i++) {
40093 for (var j = 0; j < this.allCountries.length; j++) {
40094 if(this.allCountries[j].iso2 == p[i]) {
40095 var t = this.allCountries[j];
40096 this.allCountries.splice(j,1);
40097 this.allCountries.unshift(t);
40103 this.store.proxy.data = {
40105 data: this.allCountries
40111 initEvents : function()
40114 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40116 this.indicator = this.indicatorEl();
40117 this.flag = this.flagEl();
40118 this.dialCodeHolder = this.dialCodeHolderEl();
40120 this.trigger = this.el.select('div.flag-box',true).first();
40121 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40126 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40127 _this.list.setWidth(lw);
40130 this.list.on('mouseover', this.onViewOver, this);
40131 this.list.on('mousemove', this.onViewMove, this);
40132 this.inputEl().on("keyup", this.onKeyUp, this);
40134 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40136 this.view = new Roo.View(this.list, this.tpl, {
40137 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40140 this.view.on('click', this.onViewClick, this);
40141 this.setValue(this.defaultDialCode);
40144 onTriggerClick : function(e)
40146 Roo.log('trigger click');
40151 if(this.isExpanded()){
40153 this.hasFocus = false;
40155 this.store.load({});
40156 this.hasFocus = true;
40161 isExpanded : function()
40163 return this.list.isVisible();
40166 collapse : function()
40168 if(!this.isExpanded()){
40172 Roo.get(document).un('mousedown', this.collapseIf, this);
40173 Roo.get(document).un('mousewheel', this.collapseIf, this);
40174 this.fireEvent('collapse', this);
40178 expand : function()
40182 if(this.isExpanded() || !this.hasFocus){
40186 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40187 this.list.setWidth(lw);
40190 this.restrictHeight();
40192 Roo.get(document).on('mousedown', this.collapseIf, this);
40193 Roo.get(document).on('mousewheel', this.collapseIf, this);
40195 this.fireEvent('expand', this);
40198 restrictHeight : function()
40200 this.list.alignTo(this.inputEl(), this.listAlign);
40201 this.list.alignTo(this.inputEl(), this.listAlign);
40204 onViewOver : function(e, t)
40206 if(this.inKeyMode){
40209 var item = this.view.findItemFromChild(t);
40212 var index = this.view.indexOf(item);
40213 this.select(index, false);
40218 onViewClick : function(view, doFocus, el, e)
40220 var index = this.view.getSelectedIndexes()[0];
40222 var r = this.store.getAt(index);
40225 this.onSelect(r, index);
40227 if(doFocus !== false && !this.blockFocus){
40228 this.inputEl().focus();
40232 onViewMove : function(e, t)
40234 this.inKeyMode = false;
40237 select : function(index, scrollIntoView)
40239 this.selectedIndex = index;
40240 this.view.select(index);
40241 if(scrollIntoView !== false){
40242 var el = this.view.getNode(index);
40244 this.list.scrollChildIntoView(el, false);
40249 createList : function()
40251 this.list = Roo.get(document.body).createChild({
40253 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40254 style: 'display:none'
40257 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40260 collapseIf : function(e)
40262 var in_combo = e.within(this.el);
40263 var in_list = e.within(this.list);
40264 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40266 if (in_combo || in_list || is_list) {
40272 onSelect : function(record, index)
40274 if(this.fireEvent('beforeselect', this, record, index) !== false){
40276 this.setFlagClass(record.data.iso2);
40277 this.setDialCode(record.data.dialCode);
40278 this.hasFocus = false;
40280 this.fireEvent('select', this, record, index);
40284 flagEl : function()
40286 var flag = this.el.select('div.flag',true).first();
40293 dialCodeHolderEl : function()
40295 var d = this.el.select('input.dial-code-holder',true).first();
40302 setDialCode : function(v)
40304 this.dialCodeHolder.dom.value = '+'+v;
40307 setFlagClass : function(n)
40309 this.flag.dom.className = 'flag '+n;
40312 getValue : function()
40314 var v = this.inputEl().getValue();
40315 if(this.dialCodeHolder) {
40316 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40321 setValue : function(v)
40323 var d = this.getDialCode(v);
40325 //invalid dial code
40326 if(v.length == 0 || !d || d.length == 0) {
40328 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40329 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40335 this.setFlagClass(this.dialCodeMapping[d].iso2);
40336 this.setDialCode(d);
40337 this.inputEl().dom.value = v.replace('+'+d,'');
40338 this.hiddenEl().dom.value = this.getValue();
40343 getDialCode : function(v)
40347 if (v.length == 0) {
40348 return this.dialCodeHolder.dom.value;
40352 if (v.charAt(0) != "+") {
40355 var numericChars = "";
40356 for (var i = 1; i < v.length; i++) {
40357 var c = v.charAt(i);
40360 if (this.dialCodeMapping[numericChars]) {
40361 dialCode = v.substr(1, i);
40363 if (numericChars.length == 4) {
40373 this.setValue(this.defaultDialCode);
40377 hiddenEl : function()
40379 return this.el.select('input.hidden-tel-input',true).first();
40382 onKeyUp : function(e){
40384 var k = e.getKey();
40385 var c = e.getCharCode();
40388 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40389 this.allowed.indexOf(String.fromCharCode(c)) === -1
40394 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40397 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40401 this.setValue(this.getValue());
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,
40486 getAutoCreate : function()
40488 var align = this.labelAlign || this.parentLabelAlign();
40500 cls : 'form-control roo-money-amount-input',
40501 autocomplete: 'new-password'
40504 var hiddenInput = {
40508 cls: 'hidden-number-input'
40512 hiddenInput.name = this.name;
40515 if (this.disabled) {
40516 input.disabled = true;
40519 var clg = 12 - this.inputlg;
40520 var cmd = 12 - this.inputmd;
40521 var csm = 12 - this.inputsm;
40522 var cxs = 12 - this.inputxs;
40526 cls : 'row roo-money-field',
40530 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40534 cls: 'roo-select2-container input-group',
40538 cls : 'form-control roo-money-currency-input',
40539 autocomplete: 'new-password',
40541 name : this.currencyName
40545 cls : 'input-group-addon',
40559 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40563 cls: this.hasFeedback ? 'has-feedback' : '',
40574 if (this.fieldLabel.length) {
40577 tooltip: 'This field is required'
40583 cls: 'control-label',
40589 html: this.fieldLabel
40592 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40598 if(this.indicatorpos == 'right') {
40599 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40606 if(align == 'left') {
40614 if(this.labelWidth > 12){
40615 label.style = "width: " + this.labelWidth + 'px';
40617 if(this.labelWidth < 13 && this.labelmd == 0){
40618 this.labelmd = this.labelWidth;
40620 if(this.labellg > 0){
40621 label.cls += ' col-lg-' + this.labellg;
40622 input.cls += ' col-lg-' + (12 - this.labellg);
40624 if(this.labelmd > 0){
40625 label.cls += ' col-md-' + this.labelmd;
40626 container.cls += ' col-md-' + (12 - this.labelmd);
40628 if(this.labelsm > 0){
40629 label.cls += ' col-sm-' + this.labelsm;
40630 container.cls += ' col-sm-' + (12 - this.labelsm);
40632 if(this.labelxs > 0){
40633 label.cls += ' col-xs-' + this.labelxs;
40634 container.cls += ' col-xs-' + (12 - this.labelxs);
40645 var settings = this;
40647 ['xs','sm','md','lg'].map(function(size){
40648 if (settings[size]) {
40649 cfg.cls += ' col-' + size + '-' + settings[size];
40656 initEvents : function()
40658 this.indicator = this.indicatorEl();
40660 this.initCurrencyEvent();
40662 this.initNumberEvent();
40665 initCurrencyEvent : function()
40668 throw "can not find store for combo";
40671 this.store = Roo.factory(this.store, Roo.data);
40672 this.store.parent = this;
40676 this.triggerEl = this.el.select('.input-group-addon', true).first();
40678 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40683 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40684 _this.list.setWidth(lw);
40687 this.list.on('mouseover', this.onViewOver, this);
40688 this.list.on('mousemove', this.onViewMove, this);
40689 this.list.on('scroll', this.onViewScroll, this);
40692 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40695 this.view = new Roo.View(this.list, this.tpl, {
40696 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40699 this.view.on('click', this.onViewClick, this);
40701 this.store.on('beforeload', this.onBeforeLoad, this);
40702 this.store.on('load', this.onLoad, this);
40703 this.store.on('loadexception', this.onLoadException, this);
40705 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40706 "up" : function(e){
40707 this.inKeyMode = true;
40711 "down" : function(e){
40712 if(!this.isExpanded()){
40713 this.onTriggerClick();
40715 this.inKeyMode = true;
40720 "enter" : function(e){
40723 if(this.fireEvent("specialkey", this, e)){
40724 this.onViewClick(false);
40730 "esc" : function(e){
40734 "tab" : function(e){
40737 if(this.fireEvent("specialkey", this, e)){
40738 this.onViewClick(false);
40746 doRelay : function(foo, bar, hname){
40747 if(hname == 'down' || this.scope.isExpanded()){
40748 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40756 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40760 initNumberEvent : function(e)
40762 this.inputEl().on("keydown" , this.fireKey, this);
40763 this.inputEl().on("focus", this.onFocus, this);
40764 this.inputEl().on("blur", this.onBlur, this);
40766 this.inputEl().relayEvent('keyup', this);
40768 if(this.indicator){
40769 this.indicator.addClass('invisible');
40772 this.originalValue = this.getValue();
40774 if(this.validationEvent == 'keyup'){
40775 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40776 this.inputEl().on('keyup', this.filterValidation, this);
40778 else if(this.validationEvent !== false){
40779 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40782 if(this.selectOnFocus){
40783 this.on("focus", this.preFocus, this);
40786 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40787 this.inputEl().on("keypress", this.filterKeys, this);
40789 this.inputEl().relayEvent('keypress', this);
40792 var allowed = "0123456789";
40794 if(this.allowDecimals){
40795 allowed += this.decimalSeparator;
40798 if(this.allowNegative){
40802 if(this.thousandsDelimiter) {
40806 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40808 var keyPress = function(e){
40810 var k = e.getKey();
40812 var c = e.getCharCode();
40815 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40816 allowed.indexOf(String.fromCharCode(c)) === -1
40822 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40826 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40831 this.inputEl().on("keypress", keyPress, this);
40835 onTriggerClick : function(e)
40842 this.loadNext = false;
40844 if(this.isExpanded()){
40849 this.hasFocus = true;
40851 if(this.triggerAction == 'all') {
40852 this.doQuery(this.allQuery, true);
40856 this.doQuery(this.getRawValue());
40859 getCurrency : function()
40861 var v = this.currencyEl().getValue();
40866 restrictHeight : function()
40868 this.list.alignTo(this.currencyEl(), this.listAlign);
40869 this.list.alignTo(this.currencyEl(), this.listAlign);
40872 onViewClick : function(view, doFocus, el, e)
40874 var index = this.view.getSelectedIndexes()[0];
40876 var r = this.store.getAt(index);
40879 this.onSelect(r, index);
40883 onSelect : function(record, index){
40885 if(this.fireEvent('beforeselect', this, record, index) !== false){
40887 this.setFromCurrencyData(index > -1 ? record.data : false);
40891 this.fireEvent('select', this, record, index);
40895 setFromCurrencyData : function(o)
40899 this.lastCurrency = o;
40901 if (this.currencyField) {
40902 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40904 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40907 this.lastSelectionText = currency;
40909 //setting default currency
40910 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40911 this.setCurrency(this.defaultCurrency);
40915 this.setCurrency(currency);
40918 setFromData : function(o)
40922 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40924 this.setFromCurrencyData(c);
40929 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40931 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40934 this.setValue(value);
40938 setCurrency : function(v)
40940 this.currencyValue = v;
40943 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40948 setValue : function(v)
40950 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40956 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40958 this.inputEl().dom.value = (v == '') ? '' :
40959 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40961 if(!this.allowZero && v === '0') {
40962 this.hiddenEl().dom.value = '';
40963 this.inputEl().dom.value = '';
40970 getRawValue : function()
40972 var v = this.inputEl().getValue();
40977 getValue : function()
40979 return this.fixPrecision(this.parseValue(this.getRawValue()));
40982 parseValue : function(value)
40984 if(this.thousandsDelimiter) {
40986 r = new RegExp(",", "g");
40987 value = value.replace(r, "");
40990 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40991 return isNaN(value) ? '' : value;
40995 fixPrecision : function(value)
40997 if(this.thousandsDelimiter) {
40999 r = new RegExp(",", "g");
41000 value = value.replace(r, "");
41003 var nan = isNaN(value);
41005 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41006 return nan ? '' : value;
41008 return parseFloat(value).toFixed(this.decimalPrecision);
41011 decimalPrecisionFcn : function(v)
41013 return Math.floor(v);
41016 validateValue : function(value)
41018 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41022 var num = this.parseValue(value);
41025 this.markInvalid(String.format(this.nanText, value));
41029 if(num < this.minValue){
41030 this.markInvalid(String.format(this.minText, this.minValue));
41034 if(num > this.maxValue){
41035 this.markInvalid(String.format(this.maxText, this.maxValue));
41042 validate : function()
41044 if(this.disabled || this.allowBlank){
41049 var currency = this.getCurrency();
41051 if(this.validateValue(this.getRawValue()) && currency.length){
41056 this.markInvalid();
41060 getName: function()
41065 beforeBlur : function()
41071 var v = this.parseValue(this.getRawValue());
41078 onBlur : function()
41082 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41083 //this.el.removeClass(this.focusClass);
41086 this.hasFocus = false;
41088 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41092 var v = this.getValue();
41094 if(String(v) !== String(this.startValue)){
41095 this.fireEvent('change', this, v, this.startValue);
41098 this.fireEvent("blur", this);
41101 inputEl : function()
41103 return this.el.select('.roo-money-amount-input', true).first();
41106 currencyEl : function()
41108 return this.el.select('.roo-money-currency-input', true).first();
41111 hiddenEl : function()
41113 return this.el.select('input.hidden-number-input',true).first();