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;
929 setWeight : function(str)
931 this.el.removeClass(this.weightClass);
932 this.el.addClass('btn-' + str);
946 * @class Roo.bootstrap.Column
947 * @extends Roo.bootstrap.Component
948 * Bootstrap Column class
949 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
950 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
951 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
952 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
953 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
954 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
955 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
956 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
959 * @cfg {Boolean} hidden (true|false) hide the element
960 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
961 * @cfg {String} fa (ban|check|...) font awesome icon
962 * @cfg {Number} fasize (1|2|....) font awsome size
964 * @cfg {String} icon (info-sign|check|...) glyphicon name
966 * @cfg {String} html content of column.
969 * Create a new Column
970 * @param {Object} config The config object
973 Roo.bootstrap.Column = function(config){
974 Roo.bootstrap.Column.superclass.constructor.call(this, config);
977 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
995 getAutoCreate : function(){
996 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1004 ['xs','sm','md','lg'].map(function(size){
1005 //Roo.log( size + ':' + settings[size]);
1007 if (settings[size+'off'] !== false) {
1008 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1011 if (settings[size] === false) {
1015 if (!settings[size]) { // 0 = hidden
1016 cfg.cls += ' hidden-' + size;
1019 cfg.cls += ' col-' + size + '-' + settings[size];
1024 cfg.cls += ' hidden';
1027 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1028 cfg.cls +=' alert alert-' + this.alert;
1032 if (this.html.length) {
1033 cfg.html = this.html;
1037 if (this.fasize > 1) {
1038 fasize = ' fa-' + this.fasize + 'x';
1040 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1045 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1064 * @class Roo.bootstrap.Container
1065 * @extends Roo.bootstrap.Component
1066 * Bootstrap Container class
1067 * @cfg {Boolean} jumbotron is it a jumbotron element
1068 * @cfg {String} html content of element
1069 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1070 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1071 * @cfg {String} header content of header (for panel)
1072 * @cfg {String} footer content of footer (for panel)
1073 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1074 * @cfg {String} tag (header|aside|section) type of HTML tag.
1075 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1076 * @cfg {String} fa font awesome icon
1077 * @cfg {String} icon (info-sign|check|...) glyphicon name
1078 * @cfg {Boolean} hidden (true|false) hide the element
1079 * @cfg {Boolean} expandable (true|false) default false
1080 * @cfg {Boolean} expanded (true|false) default true
1081 * @cfg {String} rheader contet on the right of header
1082 * @cfg {Boolean} clickable (true|false) default false
1086 * Create a new Container
1087 * @param {Object} config The config object
1090 Roo.bootstrap.Container = function(config){
1091 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1097 * After the panel has been expand
1099 * @param {Roo.bootstrap.Container} this
1104 * After the panel has been collapsed
1106 * @param {Roo.bootstrap.Container} this
1111 * When a element is chick
1112 * @param {Roo.bootstrap.Container} this
1113 * @param {Roo.EventObject} e
1119 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1137 getChildContainer : function() {
1143 if (this.panel.length) {
1144 return this.el.select('.panel-body',true).first();
1151 getAutoCreate : function(){
1154 tag : this.tag || 'div',
1158 if (this.jumbotron) {
1159 cfg.cls = 'jumbotron';
1164 // - this is applied by the parent..
1166 // cfg.cls = this.cls + '';
1169 if (this.sticky.length) {
1171 var bd = Roo.get(document.body);
1172 if (!bd.hasClass('bootstrap-sticky')) {
1173 bd.addClass('bootstrap-sticky');
1174 Roo.select('html',true).setStyle('height', '100%');
1177 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1181 if (this.well.length) {
1182 switch (this.well) {
1185 cfg.cls +=' well well-' +this.well;
1194 cfg.cls += ' hidden';
1198 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1199 cfg.cls +=' alert alert-' + this.alert;
1204 if (this.panel.length) {
1205 cfg.cls += ' panel panel-' + this.panel;
1207 if (this.header.length) {
1211 if(this.expandable){
1213 cfg.cls = cfg.cls + ' expandable';
1217 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1225 cls : 'panel-title',
1226 html : (this.expandable ? ' ' : '') + this.header
1230 cls: 'panel-header-right',
1236 cls : 'panel-heading',
1237 style : this.expandable ? 'cursor: pointer' : '',
1245 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1250 if (this.footer.length) {
1252 cls : 'panel-footer',
1261 body.html = this.html || cfg.html;
1262 // prefix with the icons..
1264 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1267 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1272 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1273 cfg.cls = 'container';
1279 initEvents: function()
1281 if(this.expandable){
1282 var headerEl = this.headerEl();
1285 headerEl.on('click', this.onToggleClick, this);
1290 this.el.on('click', this.onClick, this);
1295 onToggleClick : function()
1297 var headerEl = this.headerEl();
1313 if(this.fireEvent('expand', this)) {
1315 this.expanded = true;
1317 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1319 this.el.select('.panel-body',true).first().removeClass('hide');
1321 var toggleEl = this.toggleEl();
1327 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1332 collapse : function()
1334 if(this.fireEvent('collapse', this)) {
1336 this.expanded = false;
1338 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1339 this.el.select('.panel-body',true).first().addClass('hide');
1341 var toggleEl = this.toggleEl();
1347 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1351 toggleEl : function()
1353 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1357 return this.el.select('.panel-heading .fa',true).first();
1360 headerEl : function()
1362 if(!this.el || !this.panel.length || !this.header.length){
1366 return this.el.select('.panel-heading',true).first()
1371 if(!this.el || !this.panel.length){
1375 return this.el.select('.panel-body',true).first()
1378 titleEl : function()
1380 if(!this.el || !this.panel.length || !this.header.length){
1384 return this.el.select('.panel-title',true).first();
1387 setTitle : function(v)
1389 var titleEl = this.titleEl();
1395 titleEl.dom.innerHTML = v;
1398 getTitle : function()
1401 var titleEl = this.titleEl();
1407 return titleEl.dom.innerHTML;
1410 setRightTitle : function(v)
1412 var t = this.el.select('.panel-header-right',true).first();
1418 t.dom.innerHTML = v;
1421 onClick : function(e)
1425 this.fireEvent('click', this, e);
1438 * @class Roo.bootstrap.Img
1439 * @extends Roo.bootstrap.Component
1440 * Bootstrap Img class
1441 * @cfg {Boolean} imgResponsive false | true
1442 * @cfg {String} border rounded | circle | thumbnail
1443 * @cfg {String} src image source
1444 * @cfg {String} alt image alternative text
1445 * @cfg {String} href a tag href
1446 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1447 * @cfg {String} xsUrl xs image source
1448 * @cfg {String} smUrl sm image source
1449 * @cfg {String} mdUrl md image source
1450 * @cfg {String} lgUrl lg image source
1453 * Create a new Input
1454 * @param {Object} config The config object
1457 Roo.bootstrap.Img = function(config){
1458 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1464 * The img click event for the img.
1465 * @param {Roo.EventObject} e
1471 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1473 imgResponsive: true,
1483 getAutoCreate : function()
1485 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1486 return this.createSingleImg();
1491 cls: 'roo-image-responsive-group',
1496 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1498 if(!_this[size + 'Url']){
1504 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1505 html: _this.html || cfg.html,
1506 src: _this[size + 'Url']
1509 img.cls += ' roo-image-responsive-' + size;
1511 var s = ['xs', 'sm', 'md', 'lg'];
1513 s.splice(s.indexOf(size), 1);
1515 Roo.each(s, function(ss){
1516 img.cls += ' hidden-' + ss;
1519 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1520 cfg.cls += ' img-' + _this.border;
1524 cfg.alt = _this.alt;
1537 a.target = _this.target;
1541 cfg.cn.push((_this.href) ? a : img);
1548 createSingleImg : function()
1552 cls: (this.imgResponsive) ? 'img-responsive' : '',
1554 src : 'about:blank' // just incase src get's set to undefined?!?
1557 cfg.html = this.html || cfg.html;
1559 cfg.src = this.src || cfg.src;
1561 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1562 cfg.cls += ' img-' + this.border;
1579 a.target = this.target;
1584 return (this.href) ? a : cfg;
1587 initEvents: function()
1590 this.el.on('click', this.onClick, this);
1595 onClick : function(e)
1597 Roo.log('img onclick');
1598 this.fireEvent('click', this, e);
1601 * Sets the url of the image - used to update it
1602 * @param {String} url the url of the image
1605 setSrc : function(url)
1609 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1610 this.el.dom.src = url;
1614 this.el.select('img', true).first().dom.src = url;
1630 * @class Roo.bootstrap.Link
1631 * @extends Roo.bootstrap.Component
1632 * Bootstrap Link Class
1633 * @cfg {String} alt image alternative text
1634 * @cfg {String} href a tag href
1635 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1636 * @cfg {String} html the content of the link.
1637 * @cfg {String} anchor name for the anchor link
1638 * @cfg {String} fa - favicon
1640 * @cfg {Boolean} preventDefault (true | false) default false
1644 * Create a new Input
1645 * @param {Object} config The config object
1648 Roo.bootstrap.Link = function(config){
1649 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1655 * The img click event for the img.
1656 * @param {Roo.EventObject} e
1662 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1666 preventDefault: false,
1672 getAutoCreate : function()
1674 var html = this.html || '';
1676 if (this.fa !== false) {
1677 html = '<i class="fa fa-' + this.fa + '"></i>';
1682 // anchor's do not require html/href...
1683 if (this.anchor === false) {
1685 cfg.href = this.href || '#';
1687 cfg.name = this.anchor;
1688 if (this.html !== false || this.fa !== false) {
1691 if (this.href !== false) {
1692 cfg.href = this.href;
1696 if(this.alt !== false){
1701 if(this.target !== false) {
1702 cfg.target = this.target;
1708 initEvents: function() {
1710 if(!this.href || this.preventDefault){
1711 this.el.on('click', this.onClick, this);
1715 onClick : function(e)
1717 if(this.preventDefault){
1720 //Roo.log('img onclick');
1721 this.fireEvent('click', this, e);
1734 * @class Roo.bootstrap.Header
1735 * @extends Roo.bootstrap.Component
1736 * Bootstrap Header class
1737 * @cfg {String} html content of header
1738 * @cfg {Number} level (1|2|3|4|5|6) default 1
1741 * Create a new Header
1742 * @param {Object} config The config object
1746 Roo.bootstrap.Header = function(config){
1747 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1750 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1758 getAutoCreate : function(){
1763 tag: 'h' + (1 *this.level),
1764 html: this.html || ''
1776 * Ext JS Library 1.1.1
1777 * Copyright(c) 2006-2007, Ext JS, LLC.
1779 * Originally Released Under LGPL - original licence link has changed is not relivant.
1782 * <script type="text/javascript">
1786 * @class Roo.bootstrap.MenuMgr
1787 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1790 Roo.bootstrap.MenuMgr = function(){
1791 var menus, active, groups = {}, attached = false, lastShow = new Date();
1793 // private - called when first menu is created
1796 active = new Roo.util.MixedCollection();
1797 Roo.get(document).addKeyListener(27, function(){
1798 if(active.length > 0){
1806 if(active && active.length > 0){
1807 var c = active.clone();
1817 if(active.length < 1){
1818 Roo.get(document).un("mouseup", onMouseDown);
1826 var last = active.last();
1827 lastShow = new Date();
1830 Roo.get(document).on("mouseup", onMouseDown);
1835 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1836 m.parentMenu.activeChild = m;
1837 }else if(last && last.isVisible()){
1838 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1843 function onBeforeHide(m){
1845 m.activeChild.hide();
1847 if(m.autoHideTimer){
1848 clearTimeout(m.autoHideTimer);
1849 delete m.autoHideTimer;
1854 function onBeforeShow(m){
1855 var pm = m.parentMenu;
1856 if(!pm && !m.allowOtherMenus){
1858 }else if(pm && pm.activeChild && active != m){
1859 pm.activeChild.hide();
1863 // private this should really trigger on mouseup..
1864 function onMouseDown(e){
1865 Roo.log("on Mouse Up");
1867 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1868 Roo.log("MenuManager hideAll");
1877 function onBeforeCheck(mi, state){
1879 var g = groups[mi.group];
1880 for(var i = 0, l = g.length; i < l; i++){
1882 g[i].setChecked(false);
1891 * Hides all menus that are currently visible
1893 hideAll : function(){
1898 register : function(menu){
1902 menus[menu.id] = menu;
1903 menu.on("beforehide", onBeforeHide);
1904 menu.on("hide", onHide);
1905 menu.on("beforeshow", onBeforeShow);
1906 menu.on("show", onShow);
1908 if(g && menu.events["checkchange"]){
1912 groups[g].push(menu);
1913 menu.on("checkchange", onCheck);
1918 * Returns a {@link Roo.menu.Menu} object
1919 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1920 * be used to generate and return a new Menu instance.
1922 get : function(menu){
1923 if(typeof menu == "string"){ // menu id
1925 }else if(menu.events){ // menu instance
1928 /*else if(typeof menu.length == 'number'){ // array of menu items?
1929 return new Roo.bootstrap.Menu({items:menu});
1930 }else{ // otherwise, must be a config
1931 return new Roo.bootstrap.Menu(menu);
1938 unregister : function(menu){
1939 delete menus[menu.id];
1940 menu.un("beforehide", onBeforeHide);
1941 menu.un("hide", onHide);
1942 menu.un("beforeshow", onBeforeShow);
1943 menu.un("show", onShow);
1945 if(g && menu.events["checkchange"]){
1946 groups[g].remove(menu);
1947 menu.un("checkchange", onCheck);
1952 registerCheckable : function(menuItem){
1953 var g = menuItem.group;
1958 groups[g].push(menuItem);
1959 menuItem.on("beforecheckchange", onBeforeCheck);
1964 unregisterCheckable : function(menuItem){
1965 var g = menuItem.group;
1967 groups[g].remove(menuItem);
1968 menuItem.un("beforecheckchange", onBeforeCheck);
1980 * @class Roo.bootstrap.Menu
1981 * @extends Roo.bootstrap.Component
1982 * Bootstrap Menu class - container for MenuItems
1983 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1984 * @cfg {bool} hidden if the menu should be hidden when rendered.
1985 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1986 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1990 * @param {Object} config The config object
1994 Roo.bootstrap.Menu = function(config){
1995 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1996 if (this.registerMenu && this.type != 'treeview') {
1997 Roo.bootstrap.MenuMgr.register(this);
2002 * Fires before this menu is displayed
2003 * @param {Roo.menu.Menu} this
2008 * Fires before this menu is hidden
2009 * @param {Roo.menu.Menu} this
2014 * Fires after this menu is displayed
2015 * @param {Roo.menu.Menu} this
2020 * Fires after this menu is hidden
2021 * @param {Roo.menu.Menu} this
2026 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2027 * @param {Roo.menu.Menu} this
2028 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2029 * @param {Roo.EventObject} e
2034 * Fires when the mouse is hovering over 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 the mouse exits this menu
2043 * @param {Roo.menu.Menu} this
2044 * @param {Roo.EventObject} e
2045 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2050 * Fires when a menu item contained in this menu is clicked
2051 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2052 * @param {Roo.EventObject} e
2056 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2059 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2063 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2066 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2068 registerMenu : true,
2070 menuItems :false, // stores the menu items..
2080 getChildContainer : function() {
2084 getAutoCreate : function(){
2086 //if (['right'].indexOf(this.align)!==-1) {
2087 // cfg.cn[1].cls += ' pull-right'
2093 cls : 'dropdown-menu' ,
2094 style : 'z-index:1000'
2098 if (this.type === 'submenu') {
2099 cfg.cls = 'submenu active';
2101 if (this.type === 'treeview') {
2102 cfg.cls = 'treeview-menu';
2107 initEvents : function() {
2109 // Roo.log("ADD event");
2110 // Roo.log(this.triggerEl.dom);
2112 this.triggerEl.on('click', this.onTriggerClick, this);
2114 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2116 this.triggerEl.addClass('dropdown-toggle');
2119 this.el.on('touchstart' , this.onTouch, this);
2121 this.el.on('click' , this.onClick, this);
2123 this.el.on("mouseover", this.onMouseOver, this);
2124 this.el.on("mouseout", this.onMouseOut, this);
2128 findTargetItem : function(e)
2130 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2134 //Roo.log(t); Roo.log(t.id);
2136 //Roo.log(this.menuitems);
2137 return this.menuitems.get(t.id);
2139 //return this.items.get(t.menuItemId);
2145 onTouch : function(e)
2147 Roo.log("menu.onTouch");
2148 //e.stopEvent(); this make the user popdown broken
2152 onClick : function(e)
2154 Roo.log("menu.onClick");
2156 var t = this.findTargetItem(e);
2157 if(!t || t.isContainer){
2162 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2163 if(t == this.activeItem && t.shouldDeactivate(e)){
2164 this.activeItem.deactivate();
2165 delete this.activeItem;
2169 this.setActiveItem(t, true);
2177 Roo.log('pass click event');
2181 this.fireEvent("click", this, t, e);
2185 if(!t.href.length || t.href == '#'){
2186 (function() { _this.hide(); }).defer(100);
2191 onMouseOver : function(e){
2192 var t = this.findTargetItem(e);
2195 // if(t.canActivate && !t.disabled){
2196 // this.setActiveItem(t, true);
2200 this.fireEvent("mouseover", this, e, t);
2202 isVisible : function(){
2203 return !this.hidden;
2205 onMouseOut : function(e){
2206 var t = this.findTargetItem(e);
2209 // if(t == this.activeItem && t.shouldDeactivate(e)){
2210 // this.activeItem.deactivate();
2211 // delete this.activeItem;
2214 this.fireEvent("mouseout", this, e, t);
2219 * Displays this menu relative to another element
2220 * @param {String/HTMLElement/Roo.Element} element The element to align to
2221 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2222 * the element (defaults to this.defaultAlign)
2223 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2225 show : function(el, pos, parentMenu){
2226 this.parentMenu = parentMenu;
2230 this.fireEvent("beforeshow", this);
2231 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2234 * Displays this menu at a specific xy position
2235 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2236 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2238 showAt : function(xy, parentMenu, /* private: */_e){
2239 this.parentMenu = parentMenu;
2244 this.fireEvent("beforeshow", this);
2245 //xy = this.el.adjustForConstraints(xy);
2249 this.hideMenuItems();
2250 this.hidden = false;
2251 this.triggerEl.addClass('open');
2253 // reassign x when hitting right
2254 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2255 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2258 // reassign y when hitting bottom
2259 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2260 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2263 // but the list may align on trigger left or trigger top... should it be a properity?
2265 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2270 this.fireEvent("show", this);
2276 this.doFocus.defer(50, this);
2280 doFocus : function(){
2282 this.focusEl.focus();
2287 * Hides this menu and optionally all parent menus
2288 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2290 hide : function(deep)
2293 this.hideMenuItems();
2294 if(this.el && this.isVisible()){
2295 this.fireEvent("beforehide", this);
2296 if(this.activeItem){
2297 this.activeItem.deactivate();
2298 this.activeItem = null;
2300 this.triggerEl.removeClass('open');;
2302 this.fireEvent("hide", this);
2304 if(deep === true && this.parentMenu){
2305 this.parentMenu.hide(true);
2309 onTriggerClick : function(e)
2311 Roo.log('trigger click');
2313 var target = e.getTarget();
2315 Roo.log(target.nodeName.toLowerCase());
2317 if(target.nodeName.toLowerCase() === 'i'){
2323 onTriggerPress : function(e)
2325 Roo.log('trigger press');
2326 //Roo.log(e.getTarget());
2327 // Roo.log(this.triggerEl.dom);
2329 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2330 var pel = Roo.get(e.getTarget());
2331 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2332 Roo.log('is treeview or dropdown?');
2336 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2340 if (this.isVisible()) {
2345 this.show(this.triggerEl, false, false);
2348 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2355 hideMenuItems : function()
2357 Roo.log("hide Menu Items");
2361 //$(backdrop).remove()
2362 this.el.select('.open',true).each(function(aa) {
2364 aa.removeClass('open');
2365 //var parent = getParent($(this))
2366 //var relatedTarget = { relatedTarget: this }
2368 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2369 //if (e.isDefaultPrevented()) return
2370 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2373 addxtypeChild : function (tree, cntr) {
2374 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2376 this.menuitems.add(comp);
2388 this.getEl().dom.innerHTML = '';
2389 this.menuitems.clear();
2403 * @class Roo.bootstrap.MenuItem
2404 * @extends Roo.bootstrap.Component
2405 * Bootstrap MenuItem class
2406 * @cfg {String} html the menu label
2407 * @cfg {String} href the link
2408 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2409 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2410 * @cfg {Boolean} active used on sidebars to highlight active itesm
2411 * @cfg {String} fa favicon to show on left of menu item.
2412 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2416 * Create a new MenuItem
2417 * @param {Object} config The config object
2421 Roo.bootstrap.MenuItem = function(config){
2422 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2427 * The raw click event for the entire grid.
2428 * @param {Roo.bootstrap.MenuItem} this
2429 * @param {Roo.EventObject} e
2435 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2439 preventDefault: false,
2440 isContainer : false,
2444 getAutoCreate : function(){
2446 if(this.isContainer){
2449 cls: 'dropdown-menu-item'
2463 if (this.fa !== false) {
2466 cls : 'fa fa-' + this.fa
2475 cls: 'dropdown-menu-item',
2478 if (this.parent().type == 'treeview') {
2479 cfg.cls = 'treeview-menu';
2482 cfg.cls += ' active';
2487 anc.href = this.href || cfg.cn[0].href ;
2488 ctag.html = this.html || cfg.cn[0].html ;
2492 initEvents: function()
2494 if (this.parent().type == 'treeview') {
2495 this.el.select('a').on('click', this.onClick, this);
2499 this.menu.parentType = this.xtype;
2500 this.menu.triggerEl = this.el;
2501 this.menu = this.addxtype(Roo.apply({}, this.menu));
2505 onClick : function(e)
2507 Roo.log('item on click ');
2509 if(this.preventDefault){
2512 //this.parent().hideMenuItems();
2514 this.fireEvent('click', this, e);
2533 * @class Roo.bootstrap.MenuSeparator
2534 * @extends Roo.bootstrap.Component
2535 * Bootstrap MenuSeparator class
2538 * Create a new MenuItem
2539 * @param {Object} config The config object
2543 Roo.bootstrap.MenuSeparator = function(config){
2544 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2547 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2549 getAutoCreate : function(){
2568 * @class Roo.bootstrap.Modal
2569 * @extends Roo.bootstrap.Component
2570 * Bootstrap Modal class
2571 * @cfg {String} title Title of dialog
2572 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2573 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2574 * @cfg {Boolean} specificTitle default false
2575 * @cfg {Array} buttons Array of buttons or standard button set..
2576 * @cfg {String} buttonPosition (left|right|center) default right
2577 * @cfg {Boolean} animate default true
2578 * @cfg {Boolean} allow_close default true
2579 * @cfg {Boolean} fitwindow default false
2580 * @cfg {String} size (sm|lg) default empty
2581 * @cfg {Number} max_width set the max width of modal
2585 * Create a new Modal Dialog
2586 * @param {Object} config The config object
2589 Roo.bootstrap.Modal = function(config){
2590 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2595 * The raw btnclick event for the button
2596 * @param {Roo.EventObject} e
2601 * Fire when dialog resize
2602 * @param {Roo.bootstrap.Modal} this
2603 * @param {Roo.EventObject} e
2607 this.buttons = this.buttons || [];
2610 this.tmpl = Roo.factory(this.tmpl);
2615 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2617 title : 'test dialog',
2627 specificTitle: false,
2629 buttonPosition: 'right',
2651 onRender : function(ct, position)
2653 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2656 var cfg = Roo.apply({}, this.getAutoCreate());
2659 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2661 //if (!cfg.name.length) {
2665 cfg.cls += ' ' + this.cls;
2668 cfg.style = this.style;
2670 this.el = Roo.get(document.body).createChild(cfg, position);
2672 //var type = this.el.dom.type;
2675 if(this.tabIndex !== undefined){
2676 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2679 this.dialogEl = this.el.select('.modal-dialog',true).first();
2680 this.bodyEl = this.el.select('.modal-body',true).first();
2681 this.closeEl = this.el.select('.modal-header .close', true).first();
2682 this.headerEl = this.el.select('.modal-header',true).first();
2683 this.titleEl = this.el.select('.modal-title',true).first();
2684 this.footerEl = this.el.select('.modal-footer',true).first();
2686 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2688 //this.el.addClass("x-dlg-modal");
2690 if (this.buttons.length) {
2691 Roo.each(this.buttons, function(bb) {
2692 var b = Roo.apply({}, bb);
2693 b.xns = b.xns || Roo.bootstrap;
2694 b.xtype = b.xtype || 'Button';
2695 if (typeof(b.listeners) == 'undefined') {
2696 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2699 var btn = Roo.factory(b);
2701 btn.render(this.el.select('.modal-footer div').first());
2705 // render the children.
2708 if(typeof(this.items) != 'undefined'){
2709 var items = this.items;
2712 for(var i =0;i < items.length;i++) {
2713 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2717 this.items = nitems;
2719 // where are these used - they used to be body/close/footer
2723 //this.el.addClass([this.fieldClass, this.cls]);
2727 getAutoCreate : function()
2731 html : this.html || ''
2736 cls : 'modal-title',
2740 if(this.specificTitle){
2746 if (this.allow_close) {
2758 if(this.size.length){
2759 size = 'modal-' + this.size;
2766 cls: "modal-dialog " + size,
2769 cls : "modal-content",
2772 cls : 'modal-header',
2777 cls : 'modal-footer',
2781 cls: 'btn-' + this.buttonPosition
2798 modal.cls += ' fade';
2804 getChildContainer : function() {
2809 getButtonContainer : function() {
2810 return this.el.select('.modal-footer div',true).first();
2813 initEvents : function()
2815 if (this.allow_close) {
2816 this.closeEl.on('click', this.hide, this);
2818 Roo.EventManager.onWindowResize(this.resize, this, true);
2825 this.maskEl.setSize(
2826 Roo.lib.Dom.getViewWidth(true),
2827 Roo.lib.Dom.getViewHeight(true)
2830 if (this.fitwindow) {
2832 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2833 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2838 if(this.max_width !== 0) {
2840 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2845 this.height <= Roo.lib.Dom.getViewportHeight(true) - 60 ?
2846 this.height : Roo.lib.Dom.getViewportHeight(true) - 60
2851 if(!this.fit_content) {
2852 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2856 this.setSize(w, Math.min(
2858 this.headerEl.getHeight() +
2859 this.footerEl.getHeight() +
2860 this.getChildHeight(this.bodyEl.dom.childNodes),
2861 Roo.lib.Dom.getViewportHeight(true) - 60)
2867 setSize : function(w,h)
2878 if (!this.rendered) {
2882 //this.el.setStyle('display', 'block');
2883 this.el.removeClass('hideing');
2884 this.el.addClass('show');
2886 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2889 this.el.addClass('in');
2892 this.el.addClass('in');
2895 // not sure how we can show data in here..
2897 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2900 Roo.get(document.body).addClass("x-body-masked");
2902 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2903 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2904 this.maskEl.addClass('show');
2908 this.fireEvent('show', this);
2910 // set zindex here - otherwise it appears to be ignored...
2911 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2914 this.items.forEach( function(e) {
2915 e.layout ? e.layout() : false;
2923 if(this.fireEvent("beforehide", this) !== false){
2924 this.maskEl.removeClass('show');
2925 Roo.get(document.body).removeClass("x-body-masked");
2926 this.el.removeClass('in');
2927 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2929 if(this.animate){ // why
2930 this.el.addClass('hideing');
2932 if (!this.el.hasClass('hideing')) {
2933 return; // it's been shown again...
2935 this.el.removeClass('show');
2936 this.el.removeClass('hideing');
2940 this.el.removeClass('show');
2942 this.fireEvent('hide', this);
2945 isVisible : function()
2948 return this.el.hasClass('show') && !this.el.hasClass('hideing');
2952 addButton : function(str, cb)
2956 var b = Roo.apply({}, { html : str } );
2957 b.xns = b.xns || Roo.bootstrap;
2958 b.xtype = b.xtype || 'Button';
2959 if (typeof(b.listeners) == 'undefined') {
2960 b.listeners = { click : cb.createDelegate(this) };
2963 var btn = Roo.factory(b);
2965 btn.render(this.el.select('.modal-footer div').first());
2971 setDefaultButton : function(btn)
2973 //this.el.select('.modal-footer').()
2977 resizeTo: function(w,h)
2981 this.dialogEl.setWidth(w);
2982 if (this.diff === false) {
2983 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2986 this.bodyEl.setHeight(h - this.diff);
2988 this.fireEvent('resize', this);
2991 setContentSize : function(w, h)
2995 onButtonClick: function(btn,e)
2998 this.fireEvent('btnclick', btn.name, e);
3001 * Set the title of the Dialog
3002 * @param {String} str new Title
3004 setTitle: function(str) {
3005 this.titleEl.dom.innerHTML = str;
3008 * Set the body of the Dialog
3009 * @param {String} str new Title
3011 setBody: function(str) {
3012 this.bodyEl.dom.innerHTML = str;
3015 * Set the body of the Dialog using the template
3016 * @param {Obj} data - apply this data to the template and replace the body contents.
3018 applyBody: function(obj)
3021 Roo.log("Error - using apply Body without a template");
3024 this.tmpl.overwrite(this.bodyEl, obj);
3027 getChildHeight : function(child_nodes)
3031 child_nodes.length == 0
3036 var child_height = 0;
3038 for(var i = 0; i < child_nodes.length; i++) {
3040 // for modal with tabs...
3041 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3043 var layout_childs = child_nodes[i].childNodes;
3045 for(var j = 0; j < layout_childs.length; j++) {
3047 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3049 var layout_body_childs = layout_childs[j].childNodes;
3051 for(var k = 0; k < layout_body_childs.length; k++) {
3053 if(layout_body_childs[k].classList.contains('navbar')) {
3054 child_height += layout_body_childs[k].offsetHeight;
3055 // Roo.log('nav height: '+ layout_body_childs[k].offsetHeight);
3059 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3061 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3063 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3065 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3066 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3067 // Roo.log('active panel height: '+this.getChildHeight(layout_body_tab_childs[m].childNodes));
3081 child_height += child_nodes[i].offsetHeight;
3084 return child_height;
3090 Roo.apply(Roo.bootstrap.Modal, {
3092 * Button config that displays a single OK button
3101 * Button config that displays Yes and No buttons
3117 * Button config that displays OK and Cancel buttons
3132 * Button config that displays Yes, No and Cancel buttons
3156 * messagebox - can be used as a replace
3160 * @class Roo.MessageBox
3161 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3165 Roo.Msg.alert('Status', 'Changes saved successfully.');
3167 // Prompt for user data:
3168 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3170 // process text value...
3174 // Show a dialog using config options:
3176 title:'Save Changes?',
3177 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3178 buttons: Roo.Msg.YESNOCANCEL,
3185 Roo.bootstrap.MessageBox = function(){
3186 var dlg, opt, mask, waitTimer;
3187 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3188 var buttons, activeTextEl, bwidth;
3192 var handleButton = function(button){
3194 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3198 var handleHide = function(){
3200 dlg.el.removeClass(opt.cls);
3203 // Roo.TaskMgr.stop(waitTimer);
3204 // waitTimer = null;
3209 var updateButtons = function(b){
3212 buttons["ok"].hide();
3213 buttons["cancel"].hide();
3214 buttons["yes"].hide();
3215 buttons["no"].hide();
3216 //dlg.footer.dom.style.display = 'none';
3219 dlg.footerEl.dom.style.display = '';
3220 for(var k in buttons){
3221 if(typeof buttons[k] != "function"){
3224 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3225 width += buttons[k].el.getWidth()+15;
3235 var handleEsc = function(d, k, e){
3236 if(opt && opt.closable !== false){
3246 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3247 * @return {Roo.BasicDialog} The BasicDialog element
3249 getDialog : function(){
3251 dlg = new Roo.bootstrap.Modal( {
3254 //constraintoviewport:false,
3256 //collapsible : false,
3261 //buttonAlign:"center",
3262 closeClick : function(){
3263 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3266 handleButton("cancel");
3271 dlg.on("hide", handleHide);
3273 //dlg.addKeyListener(27, handleEsc);
3275 this.buttons = buttons;
3276 var bt = this.buttonText;
3277 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3278 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3279 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3280 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3282 bodyEl = dlg.bodyEl.createChild({
3284 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3285 '<textarea class="roo-mb-textarea"></textarea>' +
3286 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3288 msgEl = bodyEl.dom.firstChild;
3289 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3290 textboxEl.enableDisplayMode();
3291 textboxEl.addKeyListener([10,13], function(){
3292 if(dlg.isVisible() && opt && opt.buttons){
3295 }else if(opt.buttons.yes){
3296 handleButton("yes");
3300 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3301 textareaEl.enableDisplayMode();
3302 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3303 progressEl.enableDisplayMode();
3305 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3306 var pf = progressEl.dom.firstChild;
3308 pp = Roo.get(pf.firstChild);
3309 pp.setHeight(pf.offsetHeight);
3317 * Updates the message box body text
3318 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3319 * the XHTML-compliant non-breaking space character '&#160;')
3320 * @return {Roo.MessageBox} This message box
3322 updateText : function(text)
3324 if(!dlg.isVisible() && !opt.width){
3325 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3326 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3328 msgEl.innerHTML = text || ' ';
3330 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3331 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3333 Math.min(opt.width || cw , this.maxWidth),
3334 Math.max(opt.minWidth || this.minWidth, bwidth)
3337 activeTextEl.setWidth(w);
3339 if(dlg.isVisible()){
3340 dlg.fixedcenter = false;
3342 // to big, make it scroll. = But as usual stupid IE does not support
3345 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3346 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3347 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3349 bodyEl.dom.style.height = '';
3350 bodyEl.dom.style.overflowY = '';
3353 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3355 bodyEl.dom.style.overflowX = '';
3358 dlg.setContentSize(w, bodyEl.getHeight());
3359 if(dlg.isVisible()){
3360 dlg.fixedcenter = true;
3366 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3367 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3368 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3369 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3370 * @return {Roo.MessageBox} This message box
3372 updateProgress : function(value, text){
3374 this.updateText(text);
3377 if (pp) { // weird bug on my firefox - for some reason this is not defined
3378 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3379 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3385 * Returns true if the message box is currently displayed
3386 * @return {Boolean} True if the message box is visible, else false
3388 isVisible : function(){
3389 return dlg && dlg.isVisible();
3393 * Hides the message box if it is displayed
3396 if(this.isVisible()){
3402 * Displays a new message box, or reinitializes an existing message box, based on the config options
3403 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3404 * The following config object properties are supported:
3406 Property Type Description
3407 ---------- --------------- ------------------------------------------------------------------------------------
3408 animEl String/Element An id or Element from which the message box should animate as it opens and
3409 closes (defaults to undefined)
3410 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3411 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3412 closable Boolean False to hide the top-right close button (defaults to true). Note that
3413 progress and wait dialogs will ignore this property and always hide the
3414 close button as they can only be closed programmatically.
3415 cls String A custom CSS class to apply to the message box element
3416 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3417 displayed (defaults to 75)
3418 fn Function A callback function to execute after closing the dialog. The arguments to the
3419 function will be btn (the name of the button that was clicked, if applicable,
3420 e.g. "ok"), and text (the value of the active text field, if applicable).
3421 Progress and wait dialogs will ignore this option since they do not respond to
3422 user actions and can only be closed programmatically, so any required function
3423 should be called by the same code after it closes the dialog.
3424 icon String A CSS class that provides a background image to be used as an icon for
3425 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3426 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3427 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3428 modal Boolean False to allow user interaction with the page while the message box is
3429 displayed (defaults to true)
3430 msg String A string that will replace the existing message box body text (defaults
3431 to the XHTML-compliant non-breaking space character ' ')
3432 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3433 progress Boolean True to display a progress bar (defaults to false)
3434 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3435 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3436 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3437 title String The title text
3438 value String The string value to set into the active textbox element if displayed
3439 wait Boolean True to display a progress bar (defaults to false)
3440 width Number The width of the dialog in pixels
3447 msg: 'Please enter your address:',
3449 buttons: Roo.MessageBox.OKCANCEL,
3452 animEl: 'addAddressBtn'
3455 * @param {Object} config Configuration options
3456 * @return {Roo.MessageBox} This message box
3458 show : function(options)
3461 // this causes nightmares if you show one dialog after another
3462 // especially on callbacks..
3464 if(this.isVisible()){
3467 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3468 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3469 Roo.log("New Dialog Message:" + options.msg )
3470 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3471 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3474 var d = this.getDialog();
3476 d.setTitle(opt.title || " ");
3477 d.closeEl.setDisplayed(opt.closable !== false);
3478 activeTextEl = textboxEl;
3479 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3484 textareaEl.setHeight(typeof opt.multiline == "number" ?
3485 opt.multiline : this.defaultTextHeight);
3486 activeTextEl = textareaEl;
3495 progressEl.setDisplayed(opt.progress === true);
3496 this.updateProgress(0);
3497 activeTextEl.dom.value = opt.value || "";
3499 dlg.setDefaultButton(activeTextEl);
3501 var bs = opt.buttons;
3505 }else if(bs && bs.yes){
3506 db = buttons["yes"];
3508 dlg.setDefaultButton(db);
3510 bwidth = updateButtons(opt.buttons);
3511 this.updateText(opt.msg);
3513 d.el.addClass(opt.cls);
3515 d.proxyDrag = opt.proxyDrag === true;
3516 d.modal = opt.modal !== false;
3517 d.mask = opt.modal !== false ? mask : false;
3519 // force it to the end of the z-index stack so it gets a cursor in FF
3520 document.body.appendChild(dlg.el.dom);
3521 d.animateTarget = null;
3522 d.show(options.animEl);
3528 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3529 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3530 * and closing the message box when the process is complete.
3531 * @param {String} title The title bar text
3532 * @param {String} msg The message box body text
3533 * @return {Roo.MessageBox} This message box
3535 progress : function(title, msg){
3542 minWidth: this.minProgressWidth,
3549 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3550 * If a callback function is passed it will be called after the user clicks the button, and the
3551 * id of the button that was clicked will be passed as the only parameter to the callback
3552 * (could also be the top-right close button).
3553 * @param {String} title The title bar text
3554 * @param {String} msg The message box body text
3555 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3556 * @param {Object} scope (optional) The scope of the callback function
3557 * @return {Roo.MessageBox} This message box
3559 alert : function(title, msg, fn, scope)
3574 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3575 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3576 * You are responsible for closing the message box when the process is complete.
3577 * @param {String} msg The message box body text
3578 * @param {String} title (optional) The title bar text
3579 * @return {Roo.MessageBox} This message box
3581 wait : function(msg, title){
3592 waitTimer = Roo.TaskMgr.start({
3594 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3602 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3603 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3604 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3605 * @param {String} title The title bar text
3606 * @param {String} msg The message box body text
3607 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3608 * @param {Object} scope (optional) The scope of the callback function
3609 * @return {Roo.MessageBox} This message box
3611 confirm : function(title, msg, fn, scope){
3615 buttons: this.YESNO,
3624 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3625 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3626 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3627 * (could also be the top-right close button) and the text that was entered will be passed as the two
3628 * parameters to the callback.
3629 * @param {String} title The title bar text
3630 * @param {String} msg The message box body text
3631 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3632 * @param {Object} scope (optional) The scope of the callback function
3633 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3634 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3635 * @return {Roo.MessageBox} This message box
3637 prompt : function(title, msg, fn, scope, multiline){
3641 buttons: this.OKCANCEL,
3646 multiline: multiline,
3653 * Button config that displays a single OK button
3658 * Button config that displays Yes and No buttons
3661 YESNO : {yes:true, no:true},
3663 * Button config that displays OK and Cancel buttons
3666 OKCANCEL : {ok:true, cancel:true},
3668 * Button config that displays Yes, No and Cancel buttons
3671 YESNOCANCEL : {yes:true, no:true, cancel:true},
3674 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3677 defaultTextHeight : 75,
3679 * The maximum width in pixels of the message box (defaults to 600)
3684 * The minimum width in pixels of the message box (defaults to 100)
3689 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3690 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3693 minProgressWidth : 250,
3695 * An object containing the default button text strings that can be overriden for localized language support.
3696 * Supported properties are: ok, cancel, yes and no.
3697 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3710 * Shorthand for {@link Roo.MessageBox}
3712 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3713 Roo.Msg = Roo.Msg || Roo.MessageBox;
3722 * @class Roo.bootstrap.Navbar
3723 * @extends Roo.bootstrap.Component
3724 * Bootstrap Navbar class
3727 * Create a new Navbar
3728 * @param {Object} config The config object
3732 Roo.bootstrap.Navbar = function(config){
3733 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3737 * @event beforetoggle
3738 * Fire before toggle the menu
3739 * @param {Roo.EventObject} e
3741 "beforetoggle" : true
3745 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3754 getAutoCreate : function(){
3757 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3761 initEvents :function ()
3763 //Roo.log(this.el.select('.navbar-toggle',true));
3764 this.el.select('.navbar-toggle',true).on('click', function() {
3765 if(this.fireEvent('beforetoggle', this) !== false){
3766 this.el.select('.navbar-collapse',true).toggleClass('in');
3776 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3778 var size = this.el.getSize();
3779 this.maskEl.setSize(size.width, size.height);
3780 this.maskEl.enableDisplayMode("block");
3789 getChildContainer : function()
3791 if (this.el.select('.collapse').getCount()) {
3792 return this.el.select('.collapse',true).first();
3825 * @class Roo.bootstrap.NavSimplebar
3826 * @extends Roo.bootstrap.Navbar
3827 * Bootstrap Sidebar class
3829 * @cfg {Boolean} inverse is inverted color
3831 * @cfg {String} type (nav | pills | tabs)
3832 * @cfg {Boolean} arrangement stacked | justified
3833 * @cfg {String} align (left | right) alignment
3835 * @cfg {Boolean} main (true|false) main nav bar? default false
3836 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3838 * @cfg {String} tag (header|footer|nav|div) default is nav
3844 * Create a new Sidebar
3845 * @param {Object} config The config object
3849 Roo.bootstrap.NavSimplebar = function(config){
3850 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3853 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3869 getAutoCreate : function(){
3873 tag : this.tag || 'div',
3886 this.type = this.type || 'nav';
3887 if (['tabs','pills'].indexOf(this.type)!==-1) {
3888 cfg.cn[0].cls += ' nav-' + this.type
3892 if (this.type!=='nav') {
3893 Roo.log('nav type must be nav/tabs/pills')
3895 cfg.cn[0].cls += ' navbar-nav'
3901 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3902 cfg.cn[0].cls += ' nav-' + this.arrangement;
3906 if (this.align === 'right') {
3907 cfg.cn[0].cls += ' navbar-right';
3911 cfg.cls += ' navbar-inverse';
3938 * @class Roo.bootstrap.NavHeaderbar
3939 * @extends Roo.bootstrap.NavSimplebar
3940 * Bootstrap Sidebar class
3942 * @cfg {String} brand what is brand
3943 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3944 * @cfg {String} brand_href href of the brand
3945 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3946 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3947 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3948 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3951 * Create a new Sidebar
3952 * @param {Object} config The config object
3956 Roo.bootstrap.NavHeaderbar = function(config){
3957 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3961 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3968 desktopCenter : false,
3971 getAutoCreate : function(){
3974 tag: this.nav || 'nav',
3981 if (this.desktopCenter) {
3982 cn.push({cls : 'container', cn : []});
3989 cls: 'navbar-header',
3994 cls: 'navbar-toggle',
3995 'data-toggle': 'collapse',
4000 html: 'Toggle navigation'
4022 cls: 'collapse navbar-collapse',
4026 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
4028 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4029 cfg.cls += ' navbar-' + this.position;
4031 // tag can override this..
4033 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4036 if (this.brand !== '') {
4039 href: this.brand_href ? this.brand_href : '#',
4040 cls: 'navbar-brand',
4048 cfg.cls += ' main-nav';
4056 getHeaderChildContainer : function()
4058 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4059 return this.el.select('.navbar-header',true).first();
4062 return this.getChildContainer();
4066 initEvents : function()
4068 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4070 if (this.autohide) {
4075 Roo.get(document).on('scroll',function(e) {
4076 var ns = Roo.get(document).getScroll().top;
4077 var os = prevScroll;
4081 ft.removeClass('slideDown');
4082 ft.addClass('slideUp');
4085 ft.removeClass('slideUp');
4086 ft.addClass('slideDown');
4107 * @class Roo.bootstrap.NavSidebar
4108 * @extends Roo.bootstrap.Navbar
4109 * Bootstrap Sidebar class
4112 * Create a new Sidebar
4113 * @param {Object} config The config object
4117 Roo.bootstrap.NavSidebar = function(config){
4118 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4121 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4123 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4125 getAutoCreate : function(){
4130 cls: 'sidebar sidebar-nav'
4152 * @class Roo.bootstrap.NavGroup
4153 * @extends Roo.bootstrap.Component
4154 * Bootstrap NavGroup class
4155 * @cfg {String} align (left|right)
4156 * @cfg {Boolean} inverse
4157 * @cfg {String} type (nav|pills|tab) default nav
4158 * @cfg {String} navId - reference Id for navbar.
4162 * Create a new nav group
4163 * @param {Object} config The config object
4166 Roo.bootstrap.NavGroup = function(config){
4167 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4170 Roo.bootstrap.NavGroup.register(this);
4174 * Fires when the active item changes
4175 * @param {Roo.bootstrap.NavGroup} this
4176 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4177 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4184 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4195 getAutoCreate : function()
4197 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4204 if (['tabs','pills'].indexOf(this.type)!==-1) {
4205 cfg.cls += ' nav-' + this.type
4207 if (this.type!=='nav') {
4208 Roo.log('nav type must be nav/tabs/pills')
4210 cfg.cls += ' navbar-nav'
4213 if (this.parent() && this.parent().sidebar) {
4216 cls: 'dashboard-menu sidebar-menu'
4222 if (this.form === true) {
4228 if (this.align === 'right') {
4229 cfg.cls += ' navbar-right';
4231 cfg.cls += ' navbar-left';
4235 if (this.align === 'right') {
4236 cfg.cls += ' navbar-right';
4240 cfg.cls += ' navbar-inverse';
4248 * sets the active Navigation item
4249 * @param {Roo.bootstrap.NavItem} the new current navitem
4251 setActiveItem : function(item)
4254 Roo.each(this.navItems, function(v){
4259 v.setActive(false, true);
4266 item.setActive(true, true);
4267 this.fireEvent('changed', this, item, prev);
4272 * gets the active Navigation item
4273 * @return {Roo.bootstrap.NavItem} the current navitem
4275 getActive : function()
4279 Roo.each(this.navItems, function(v){
4290 indexOfNav : function()
4294 Roo.each(this.navItems, function(v,i){
4305 * adds a Navigation item
4306 * @param {Roo.bootstrap.NavItem} the navitem to add
4308 addItem : function(cfg)
4310 var cn = new Roo.bootstrap.NavItem(cfg);
4312 cn.parentId = this.id;
4313 cn.onRender(this.el, null);
4317 * register a Navigation item
4318 * @param {Roo.bootstrap.NavItem} the navitem to add
4320 register : function(item)
4322 this.navItems.push( item);
4323 item.navId = this.navId;
4328 * clear all the Navigation item
4331 clearAll : function()
4334 this.el.dom.innerHTML = '';
4337 getNavItem: function(tabId)
4340 Roo.each(this.navItems, function(e) {
4341 if (e.tabId == tabId) {
4351 setActiveNext : function()
4353 var i = this.indexOfNav(this.getActive());
4354 if (i > this.navItems.length) {
4357 this.setActiveItem(this.navItems[i+1]);
4359 setActivePrev : function()
4361 var i = this.indexOfNav(this.getActive());
4365 this.setActiveItem(this.navItems[i-1]);
4367 clearWasActive : function(except) {
4368 Roo.each(this.navItems, function(e) {
4369 if (e.tabId != except.tabId && e.was_active) {
4370 e.was_active = false;
4377 getWasActive : function ()
4380 Roo.each(this.navItems, function(e) {
4395 Roo.apply(Roo.bootstrap.NavGroup, {
4399 * register a Navigation Group
4400 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4402 register : function(navgrp)
4404 this.groups[navgrp.navId] = navgrp;
4408 * fetch a Navigation Group based on the navigation ID
4409 * @param {string} the navgroup to add
4410 * @returns {Roo.bootstrap.NavGroup} the navgroup
4412 get: function(navId) {
4413 if (typeof(this.groups[navId]) == 'undefined') {
4415 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4417 return this.groups[navId] ;
4432 * @class Roo.bootstrap.NavItem
4433 * @extends Roo.bootstrap.Component
4434 * Bootstrap Navbar.NavItem class
4435 * @cfg {String} href link to
4436 * @cfg {String} html content of button
4437 * @cfg {String} badge text inside badge
4438 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4439 * @cfg {String} glyphicon name of glyphicon
4440 * @cfg {String} icon name of font awesome icon
4441 * @cfg {Boolean} active Is item active
4442 * @cfg {Boolean} disabled Is item disabled
4444 * @cfg {Boolean} preventDefault (true | false) default false
4445 * @cfg {String} tabId the tab that this item activates.
4446 * @cfg {String} tagtype (a|span) render as a href or span?
4447 * @cfg {Boolean} animateRef (true|false) link to element default false
4450 * Create a new Navbar Item
4451 * @param {Object} config The config object
4453 Roo.bootstrap.NavItem = function(config){
4454 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4459 * The raw click event for the entire grid.
4460 * @param {Roo.EventObject} e
4465 * Fires when the active item active state changes
4466 * @param {Roo.bootstrap.NavItem} this
4467 * @param {boolean} state the new state
4473 * Fires when scroll to element
4474 * @param {Roo.bootstrap.NavItem} this
4475 * @param {Object} options
4476 * @param {Roo.EventObject} e
4484 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4492 preventDefault : false,
4499 getAutoCreate : function(){
4508 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4510 if (this.disabled) {
4511 cfg.cls += ' disabled';
4514 if (this.href || this.html || this.glyphicon || this.icon) {
4518 href : this.href || "#",
4519 html: this.html || ''
4524 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4527 if(this.glyphicon) {
4528 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4533 cfg.cn[0].html += " <span class='caret'></span>";
4537 if (this.badge !== '') {
4539 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4547 initEvents: function()
4549 if (typeof (this.menu) != 'undefined') {
4550 this.menu.parentType = this.xtype;
4551 this.menu.triggerEl = this.el;
4552 this.menu = this.addxtype(Roo.apply({}, this.menu));
4555 this.el.select('a',true).on('click', this.onClick, this);
4557 if(this.tagtype == 'span'){
4558 this.el.select('span',true).on('click', this.onClick, this);
4561 // at this point parent should be available..
4562 this.parent().register(this);
4565 onClick : function(e)
4567 if (e.getTarget('.dropdown-menu-item')) {
4568 // did you click on a menu itemm.... - then don't trigger onclick..
4573 this.preventDefault ||
4576 Roo.log("NavItem - prevent Default?");
4580 if (this.disabled) {
4584 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4585 if (tg && tg.transition) {
4586 Roo.log("waiting for the transitionend");
4592 //Roo.log("fire event clicked");
4593 if(this.fireEvent('click', this, e) === false){
4597 if(this.tagtype == 'span'){
4601 //Roo.log(this.href);
4602 var ael = this.el.select('a',true).first();
4605 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4606 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4607 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4608 return; // ignore... - it's a 'hash' to another page.
4610 Roo.log("NavItem - prevent Default?");
4612 this.scrollToElement(e);
4616 var p = this.parent();
4618 if (['tabs','pills'].indexOf(p.type)!==-1) {
4619 if (typeof(p.setActiveItem) !== 'undefined') {
4620 p.setActiveItem(this);
4624 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4625 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4626 // remove the collapsed menu expand...
4627 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4631 isActive: function () {
4634 setActive : function(state, fire, is_was_active)
4636 if (this.active && !state && this.navId) {
4637 this.was_active = true;
4638 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4640 nv.clearWasActive(this);
4644 this.active = state;
4647 this.el.removeClass('active');
4648 } else if (!this.el.hasClass('active')) {
4649 this.el.addClass('active');
4652 this.fireEvent('changed', this, state);
4655 // show a panel if it's registered and related..
4657 if (!this.navId || !this.tabId || !state || is_was_active) {
4661 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4665 var pan = tg.getPanelByName(this.tabId);
4669 // if we can not flip to new panel - go back to old nav highlight..
4670 if (false == tg.showPanel(pan)) {
4671 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4673 var onav = nv.getWasActive();
4675 onav.setActive(true, false, true);
4684 // this should not be here...
4685 setDisabled : function(state)
4687 this.disabled = state;
4689 this.el.removeClass('disabled');
4690 } else if (!this.el.hasClass('disabled')) {
4691 this.el.addClass('disabled');
4697 * Fetch the element to display the tooltip on.
4698 * @return {Roo.Element} defaults to this.el
4700 tooltipEl : function()
4702 return this.el.select('' + this.tagtype + '', true).first();
4705 scrollToElement : function(e)
4707 var c = document.body;
4710 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4712 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4713 c = document.documentElement;
4716 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4722 var o = target.calcOffsetsTo(c);
4729 this.fireEvent('scrollto', this, options, e);
4731 Roo.get(c).scrollTo('top', options.value, true);
4744 * <span> icon </span>
4745 * <span> text </span>
4746 * <span>badge </span>
4750 * @class Roo.bootstrap.NavSidebarItem
4751 * @extends Roo.bootstrap.NavItem
4752 * Bootstrap Navbar.NavSidebarItem class
4753 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4754 * {Boolean} open is the menu open
4755 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4756 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4757 * {String} buttonSize (sm|md|lg)the extra classes for the button
4758 * {Boolean} showArrow show arrow next to the text (default true)
4760 * Create a new Navbar Button
4761 * @param {Object} config The config object
4763 Roo.bootstrap.NavSidebarItem = function(config){
4764 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4769 * The raw click event for the entire grid.
4770 * @param {Roo.EventObject} e
4775 * Fires when the active item active state changes
4776 * @param {Roo.bootstrap.NavSidebarItem} this
4777 * @param {boolean} state the new state
4785 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4787 badgeWeight : 'default',
4793 buttonWeight : 'default',
4799 getAutoCreate : function(){
4804 href : this.href || '#',
4810 if(this.buttonView){
4813 href : this.href || '#',
4814 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4827 cfg.cls += ' active';
4830 if (this.disabled) {
4831 cfg.cls += ' disabled';
4834 cfg.cls += ' open x-open';
4837 if (this.glyphicon || this.icon) {
4838 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4839 a.cn.push({ tag : 'i', cls : c }) ;
4842 if(!this.buttonView){
4845 html : this.html || ''
4852 if (this.badge !== '') {
4853 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4859 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4862 a.cls += ' dropdown-toggle treeview' ;
4868 initEvents : function()
4870 if (typeof (this.menu) != 'undefined') {
4871 this.menu.parentType = this.xtype;
4872 this.menu.triggerEl = this.el;
4873 this.menu = this.addxtype(Roo.apply({}, this.menu));
4876 this.el.on('click', this.onClick, this);
4878 if(this.badge !== ''){
4879 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4884 onClick : function(e)
4891 if(this.preventDefault){
4895 this.fireEvent('click', this);
4898 disable : function()
4900 this.setDisabled(true);
4905 this.setDisabled(false);
4908 setDisabled : function(state)
4910 if(this.disabled == state){
4914 this.disabled = state;
4917 this.el.addClass('disabled');
4921 this.el.removeClass('disabled');
4926 setActive : function(state)
4928 if(this.active == state){
4932 this.active = state;
4935 this.el.addClass('active');
4939 this.el.removeClass('active');
4944 isActive: function ()
4949 setBadge : function(str)
4955 this.badgeEl.dom.innerHTML = str;
4972 * @class Roo.bootstrap.Row
4973 * @extends Roo.bootstrap.Component
4974 * Bootstrap Row class (contains columns...)
4978 * @param {Object} config The config object
4981 Roo.bootstrap.Row = function(config){
4982 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4985 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4987 getAutoCreate : function(){
5006 * @class Roo.bootstrap.Element
5007 * @extends Roo.bootstrap.Component
5008 * Bootstrap Element class
5009 * @cfg {String} html contents of the element
5010 * @cfg {String} tag tag of the element
5011 * @cfg {String} cls class of the element
5012 * @cfg {Boolean} preventDefault (true|false) default false
5013 * @cfg {Boolean} clickable (true|false) default false
5016 * Create a new Element
5017 * @param {Object} config The config object
5020 Roo.bootstrap.Element = function(config){
5021 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5027 * When a element is chick
5028 * @param {Roo.bootstrap.Element} this
5029 * @param {Roo.EventObject} e
5035 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5040 preventDefault: false,
5043 getAutoCreate : function(){
5047 // cls: this.cls, double assign in parent class Component.js :: onRender
5054 initEvents: function()
5056 Roo.bootstrap.Element.superclass.initEvents.call(this);
5059 this.el.on('click', this.onClick, this);
5064 onClick : function(e)
5066 if(this.preventDefault){
5070 this.fireEvent('click', this, e);
5073 getValue : function()
5075 return this.el.dom.innerHTML;
5078 setValue : function(value)
5080 this.el.dom.innerHTML = value;
5095 * @class Roo.bootstrap.Pagination
5096 * @extends Roo.bootstrap.Component
5097 * Bootstrap Pagination class
5098 * @cfg {String} size xs | sm | md | lg
5099 * @cfg {Boolean} inverse false | true
5102 * Create a new Pagination
5103 * @param {Object} config The config object
5106 Roo.bootstrap.Pagination = function(config){
5107 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5110 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5116 getAutoCreate : function(){
5122 cfg.cls += ' inverse';
5128 cfg.cls += " " + this.cls;
5146 * @class Roo.bootstrap.PaginationItem
5147 * @extends Roo.bootstrap.Component
5148 * Bootstrap PaginationItem class
5149 * @cfg {String} html text
5150 * @cfg {String} href the link
5151 * @cfg {Boolean} preventDefault (true | false) default true
5152 * @cfg {Boolean} active (true | false) default false
5153 * @cfg {Boolean} disabled default false
5157 * Create a new PaginationItem
5158 * @param {Object} config The config object
5162 Roo.bootstrap.PaginationItem = function(config){
5163 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5168 * The raw click event for the entire grid.
5169 * @param {Roo.EventObject} e
5175 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5179 preventDefault: true,
5184 getAutoCreate : function(){
5190 href : this.href ? this.href : '#',
5191 html : this.html ? this.html : ''
5201 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5205 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5211 initEvents: function() {
5213 this.el.on('click', this.onClick, this);
5216 onClick : function(e)
5218 Roo.log('PaginationItem on click ');
5219 if(this.preventDefault){
5227 this.fireEvent('click', this, e);
5243 * @class Roo.bootstrap.Slider
5244 * @extends Roo.bootstrap.Component
5245 * Bootstrap Slider class
5248 * Create a new Slider
5249 * @param {Object} config The config object
5252 Roo.bootstrap.Slider = function(config){
5253 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5256 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5258 getAutoCreate : function(){
5262 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5266 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5278 * Ext JS Library 1.1.1
5279 * Copyright(c) 2006-2007, Ext JS, LLC.
5281 * Originally Released Under LGPL - original licence link has changed is not relivant.
5284 * <script type="text/javascript">
5289 * @class Roo.grid.ColumnModel
5290 * @extends Roo.util.Observable
5291 * This is the default implementation of a ColumnModel used by the Grid. It defines
5292 * the columns in the grid.
5295 var colModel = new Roo.grid.ColumnModel([
5296 {header: "Ticker", width: 60, sortable: true, locked: true},
5297 {header: "Company Name", width: 150, sortable: true},
5298 {header: "Market Cap.", width: 100, sortable: true},
5299 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5300 {header: "Employees", width: 100, sortable: true, resizable: false}
5305 * The config options listed for this class are options which may appear in each
5306 * individual column definition.
5307 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5309 * @param {Object} config An Array of column config objects. See this class's
5310 * config objects for details.
5312 Roo.grid.ColumnModel = function(config){
5314 * The config passed into the constructor
5316 this.config = config;
5319 // if no id, create one
5320 // if the column does not have a dataIndex mapping,
5321 // map it to the order it is in the config
5322 for(var i = 0, len = config.length; i < len; i++){
5324 if(typeof c.dataIndex == "undefined"){
5327 if(typeof c.renderer == "string"){
5328 c.renderer = Roo.util.Format[c.renderer];
5330 if(typeof c.id == "undefined"){
5333 if(c.editor && c.editor.xtype){
5334 c.editor = Roo.factory(c.editor, Roo.grid);
5336 if(c.editor && c.editor.isFormField){
5337 c.editor = new Roo.grid.GridEditor(c.editor);
5339 this.lookup[c.id] = c;
5343 * The width of columns which have no width specified (defaults to 100)
5346 this.defaultWidth = 100;
5349 * Default sortable of columns which have no sortable specified (defaults to false)
5352 this.defaultSortable = false;
5356 * @event widthchange
5357 * Fires when the width of a column changes.
5358 * @param {ColumnModel} this
5359 * @param {Number} columnIndex The column index
5360 * @param {Number} newWidth The new width
5362 "widthchange": true,
5364 * @event headerchange
5365 * Fires when the text of a header changes.
5366 * @param {ColumnModel} this
5367 * @param {Number} columnIndex The column index
5368 * @param {Number} newText The new header text
5370 "headerchange": true,
5372 * @event hiddenchange
5373 * Fires when a column is hidden or "unhidden".
5374 * @param {ColumnModel} this
5375 * @param {Number} columnIndex The column index
5376 * @param {Boolean} hidden true if hidden, false otherwise
5378 "hiddenchange": true,
5380 * @event columnmoved
5381 * Fires when a column is moved.
5382 * @param {ColumnModel} this
5383 * @param {Number} oldIndex
5384 * @param {Number} newIndex
5386 "columnmoved" : true,
5388 * @event columlockchange
5389 * Fires when a column's locked state is changed
5390 * @param {ColumnModel} this
5391 * @param {Number} colIndex
5392 * @param {Boolean} locked true if locked
5394 "columnlockchange" : true
5396 Roo.grid.ColumnModel.superclass.constructor.call(this);
5398 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5400 * @cfg {String} header The header text to display in the Grid view.
5403 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5404 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5405 * specified, the column's index is used as an index into the Record's data Array.
5408 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5409 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5412 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5413 * Defaults to the value of the {@link #defaultSortable} property.
5414 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5417 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5420 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5423 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5426 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5429 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5430 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5431 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5432 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5435 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5438 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5441 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5444 * @cfg {String} cursor (Optional)
5447 * @cfg {String} tooltip (Optional)
5450 * @cfg {Number} xs (Optional)
5453 * @cfg {Number} sm (Optional)
5456 * @cfg {Number} md (Optional)
5459 * @cfg {Number} lg (Optional)
5462 * Returns the id of the column at the specified index.
5463 * @param {Number} index The column index
5464 * @return {String} the id
5466 getColumnId : function(index){
5467 return this.config[index].id;
5471 * Returns the column for a specified id.
5472 * @param {String} id The column id
5473 * @return {Object} the column
5475 getColumnById : function(id){
5476 return this.lookup[id];
5481 * Returns the column for a specified dataIndex.
5482 * @param {String} dataIndex The column dataIndex
5483 * @return {Object|Boolean} the column or false if not found
5485 getColumnByDataIndex: function(dataIndex){
5486 var index = this.findColumnIndex(dataIndex);
5487 return index > -1 ? this.config[index] : false;
5491 * Returns the index for a specified column id.
5492 * @param {String} id The column id
5493 * @return {Number} the index, or -1 if not found
5495 getIndexById : function(id){
5496 for(var i = 0, len = this.config.length; i < len; i++){
5497 if(this.config[i].id == id){
5505 * Returns the index for a specified column dataIndex.
5506 * @param {String} dataIndex The column dataIndex
5507 * @return {Number} the index, or -1 if not found
5510 findColumnIndex : function(dataIndex){
5511 for(var i = 0, len = this.config.length; i < len; i++){
5512 if(this.config[i].dataIndex == dataIndex){
5520 moveColumn : function(oldIndex, newIndex){
5521 var c = this.config[oldIndex];
5522 this.config.splice(oldIndex, 1);
5523 this.config.splice(newIndex, 0, c);
5524 this.dataMap = null;
5525 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5528 isLocked : function(colIndex){
5529 return this.config[colIndex].locked === true;
5532 setLocked : function(colIndex, value, suppressEvent){
5533 if(this.isLocked(colIndex) == value){
5536 this.config[colIndex].locked = value;
5538 this.fireEvent("columnlockchange", this, colIndex, value);
5542 getTotalLockedWidth : function(){
5544 for(var i = 0; i < this.config.length; i++){
5545 if(this.isLocked(i) && !this.isHidden(i)){
5546 this.totalWidth += this.getColumnWidth(i);
5552 getLockedCount : function(){
5553 for(var i = 0, len = this.config.length; i < len; i++){
5554 if(!this.isLocked(i)){
5559 return this.config.length;
5563 * Returns the number of columns.
5566 getColumnCount : function(visibleOnly){
5567 if(visibleOnly === true){
5569 for(var i = 0, len = this.config.length; i < len; i++){
5570 if(!this.isHidden(i)){
5576 return this.config.length;
5580 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5581 * @param {Function} fn
5582 * @param {Object} scope (optional)
5583 * @return {Array} result
5585 getColumnsBy : function(fn, scope){
5587 for(var i = 0, len = this.config.length; i < len; i++){
5588 var c = this.config[i];
5589 if(fn.call(scope||this, c, i) === true){
5597 * Returns true if the specified column is sortable.
5598 * @param {Number} col The column index
5601 isSortable : function(col){
5602 if(typeof this.config[col].sortable == "undefined"){
5603 return this.defaultSortable;
5605 return this.config[col].sortable;
5609 * Returns the rendering (formatting) function defined for the column.
5610 * @param {Number} col The column index.
5611 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5613 getRenderer : function(col){
5614 if(!this.config[col].renderer){
5615 return Roo.grid.ColumnModel.defaultRenderer;
5617 return this.config[col].renderer;
5621 * Sets the rendering (formatting) function for a column.
5622 * @param {Number} col The column index
5623 * @param {Function} fn The function to use to process the cell's raw data
5624 * to return HTML markup for the grid view. The render function is called with
5625 * the following parameters:<ul>
5626 * <li>Data value.</li>
5627 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5628 * <li>css A CSS style string to apply to the table cell.</li>
5629 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5630 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5631 * <li>Row index</li>
5632 * <li>Column index</li>
5633 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5635 setRenderer : function(col, fn){
5636 this.config[col].renderer = fn;
5640 * Returns the width for the specified column.
5641 * @param {Number} col The column index
5644 getColumnWidth : function(col){
5645 return this.config[col].width * 1 || this.defaultWidth;
5649 * Sets the width for a column.
5650 * @param {Number} col The column index
5651 * @param {Number} width The new width
5653 setColumnWidth : function(col, width, suppressEvent){
5654 this.config[col].width = width;
5655 this.totalWidth = null;
5657 this.fireEvent("widthchange", this, col, width);
5662 * Returns the total width of all columns.
5663 * @param {Boolean} includeHidden True to include hidden column widths
5666 getTotalWidth : function(includeHidden){
5667 if(!this.totalWidth){
5668 this.totalWidth = 0;
5669 for(var i = 0, len = this.config.length; i < len; i++){
5670 if(includeHidden || !this.isHidden(i)){
5671 this.totalWidth += this.getColumnWidth(i);
5675 return this.totalWidth;
5679 * Returns the header for the specified column.
5680 * @param {Number} col The column index
5683 getColumnHeader : function(col){
5684 return this.config[col].header;
5688 * Sets the header for a column.
5689 * @param {Number} col The column index
5690 * @param {String} header The new header
5692 setColumnHeader : function(col, header){
5693 this.config[col].header = header;
5694 this.fireEvent("headerchange", this, col, header);
5698 * Returns the tooltip for the specified column.
5699 * @param {Number} col The column index
5702 getColumnTooltip : function(col){
5703 return this.config[col].tooltip;
5706 * Sets the tooltip for a column.
5707 * @param {Number} col The column index
5708 * @param {String} tooltip The new tooltip
5710 setColumnTooltip : function(col, tooltip){
5711 this.config[col].tooltip = tooltip;
5715 * Returns the dataIndex for the specified column.
5716 * @param {Number} col The column index
5719 getDataIndex : function(col){
5720 return this.config[col].dataIndex;
5724 * Sets the dataIndex for a column.
5725 * @param {Number} col The column index
5726 * @param {Number} dataIndex The new dataIndex
5728 setDataIndex : function(col, dataIndex){
5729 this.config[col].dataIndex = dataIndex;
5735 * Returns true if the cell is editable.
5736 * @param {Number} colIndex The column index
5737 * @param {Number} rowIndex The row index - this is nto actually used..?
5740 isCellEditable : function(colIndex, rowIndex){
5741 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5745 * Returns the editor defined for the cell/column.
5746 * return false or null to disable editing.
5747 * @param {Number} colIndex The column index
5748 * @param {Number} rowIndex The row index
5751 getCellEditor : function(colIndex, rowIndex){
5752 return this.config[colIndex].editor;
5756 * Sets if a column is editable.
5757 * @param {Number} col The column index
5758 * @param {Boolean} editable True if the column is editable
5760 setEditable : function(col, editable){
5761 this.config[col].editable = editable;
5766 * Returns true if the column is hidden.
5767 * @param {Number} colIndex The column index
5770 isHidden : function(colIndex){
5771 return this.config[colIndex].hidden;
5776 * Returns true if the column width cannot be changed
5778 isFixed : function(colIndex){
5779 return this.config[colIndex].fixed;
5783 * Returns true if the column can be resized
5786 isResizable : function(colIndex){
5787 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5790 * Sets if a column is hidden.
5791 * @param {Number} colIndex The column index
5792 * @param {Boolean} hidden True if the column is hidden
5794 setHidden : function(colIndex, hidden){
5795 this.config[colIndex].hidden = hidden;
5796 this.totalWidth = null;
5797 this.fireEvent("hiddenchange", this, colIndex, hidden);
5801 * Sets the editor for a column.
5802 * @param {Number} col The column index
5803 * @param {Object} editor The editor object
5805 setEditor : function(col, editor){
5806 this.config[col].editor = editor;
5810 Roo.grid.ColumnModel.defaultRenderer = function(value)
5812 if(typeof value == "object") {
5815 if(typeof value == "string" && value.length < 1){
5819 return String.format("{0}", value);
5822 // Alias for backwards compatibility
5823 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5826 * Ext JS Library 1.1.1
5827 * Copyright(c) 2006-2007, Ext JS, LLC.
5829 * Originally Released Under LGPL - original licence link has changed is not relivant.
5832 * <script type="text/javascript">
5836 * @class Roo.LoadMask
5837 * A simple utility class for generically masking elements while loading data. If the element being masked has
5838 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5839 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5840 * element's UpdateManager load indicator and will be destroyed after the initial load.
5842 * Create a new LoadMask
5843 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5844 * @param {Object} config The config object
5846 Roo.LoadMask = function(el, config){
5847 this.el = Roo.get(el);
5848 Roo.apply(this, config);
5850 this.store.on('beforeload', this.onBeforeLoad, this);
5851 this.store.on('load', this.onLoad, this);
5852 this.store.on('loadexception', this.onLoadException, this);
5853 this.removeMask = false;
5855 var um = this.el.getUpdateManager();
5856 um.showLoadIndicator = false; // disable the default indicator
5857 um.on('beforeupdate', this.onBeforeLoad, this);
5858 um.on('update', this.onLoad, this);
5859 um.on('failure', this.onLoad, this);
5860 this.removeMask = true;
5864 Roo.LoadMask.prototype = {
5866 * @cfg {Boolean} removeMask
5867 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5868 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5872 * The text to display in a centered loading message box (defaults to 'Loading...')
5876 * @cfg {String} msgCls
5877 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5879 msgCls : 'x-mask-loading',
5882 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5888 * Disables the mask to prevent it from being displayed
5890 disable : function(){
5891 this.disabled = true;
5895 * Enables the mask so that it can be displayed
5897 enable : function(){
5898 this.disabled = false;
5901 onLoadException : function()
5905 if (typeof(arguments[3]) != 'undefined') {
5906 Roo.MessageBox.alert("Error loading",arguments[3]);
5910 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5911 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5918 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5923 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5927 onBeforeLoad : function(){
5929 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5934 destroy : function(){
5936 this.store.un('beforeload', this.onBeforeLoad, this);
5937 this.store.un('load', this.onLoad, this);
5938 this.store.un('loadexception', this.onLoadException, this);
5940 var um = this.el.getUpdateManager();
5941 um.un('beforeupdate', this.onBeforeLoad, this);
5942 um.un('update', this.onLoad, this);
5943 um.un('failure', this.onLoad, this);
5954 * @class Roo.bootstrap.Table
5955 * @extends Roo.bootstrap.Component
5956 * Bootstrap Table class
5957 * @cfg {String} cls table class
5958 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5959 * @cfg {String} bgcolor Specifies the background color for a table
5960 * @cfg {Number} border Specifies whether the table cells should have borders or not
5961 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5962 * @cfg {Number} cellspacing Specifies the space between cells
5963 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5964 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5965 * @cfg {String} sortable Specifies that the table should be sortable
5966 * @cfg {String} summary Specifies a summary of the content of a table
5967 * @cfg {Number} width Specifies the width of a table
5968 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5970 * @cfg {boolean} striped Should the rows be alternative striped
5971 * @cfg {boolean} bordered Add borders to the table
5972 * @cfg {boolean} hover Add hover highlighting
5973 * @cfg {boolean} condensed Format condensed
5974 * @cfg {boolean} responsive Format condensed
5975 * @cfg {Boolean} loadMask (true|false) default false
5976 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5977 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5978 * @cfg {Boolean} rowSelection (true|false) default false
5979 * @cfg {Boolean} cellSelection (true|false) default false
5980 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5981 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5982 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5983 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
5987 * Create a new Table
5988 * @param {Object} config The config object
5991 Roo.bootstrap.Table = function(config){
5992 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5997 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5998 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5999 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6000 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6002 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6004 this.sm.grid = this;
6005 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6006 this.sm = this.selModel;
6007 this.sm.xmodule = this.xmodule || false;
6010 if (this.cm && typeof(this.cm.config) == 'undefined') {
6011 this.colModel = new Roo.grid.ColumnModel(this.cm);
6012 this.cm = this.colModel;
6013 this.cm.xmodule = this.xmodule || false;
6016 this.store= Roo.factory(this.store, Roo.data);
6017 this.ds = this.store;
6018 this.ds.xmodule = this.xmodule || false;
6021 if (this.footer && this.store) {
6022 this.footer.dataSource = this.ds;
6023 this.footer = Roo.factory(this.footer);
6030 * Fires when a cell is clicked
6031 * @param {Roo.bootstrap.Table} this
6032 * @param {Roo.Element} el
6033 * @param {Number} rowIndex
6034 * @param {Number} columnIndex
6035 * @param {Roo.EventObject} e
6039 * @event celldblclick
6040 * Fires when a cell is double clicked
6041 * @param {Roo.bootstrap.Table} this
6042 * @param {Roo.Element} el
6043 * @param {Number} rowIndex
6044 * @param {Number} columnIndex
6045 * @param {Roo.EventObject} e
6047 "celldblclick" : true,
6050 * Fires when a row is clicked
6051 * @param {Roo.bootstrap.Table} this
6052 * @param {Roo.Element} el
6053 * @param {Number} rowIndex
6054 * @param {Roo.EventObject} e
6058 * @event rowdblclick
6059 * Fires when a row is double clicked
6060 * @param {Roo.bootstrap.Table} this
6061 * @param {Roo.Element} el
6062 * @param {Number} rowIndex
6063 * @param {Roo.EventObject} e
6065 "rowdblclick" : true,
6068 * Fires when a mouseover occur
6069 * @param {Roo.bootstrap.Table} this
6070 * @param {Roo.Element} el
6071 * @param {Number} rowIndex
6072 * @param {Number} columnIndex
6073 * @param {Roo.EventObject} e
6078 * Fires when a mouseout occur
6079 * @param {Roo.bootstrap.Table} this
6080 * @param {Roo.Element} el
6081 * @param {Number} rowIndex
6082 * @param {Number} columnIndex
6083 * @param {Roo.EventObject} e
6088 * Fires when a row is rendered, so you can change add a style to it.
6089 * @param {Roo.bootstrap.Table} this
6090 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6094 * @event rowsrendered
6095 * Fires when all the rows have been rendered
6096 * @param {Roo.bootstrap.Table} this
6098 'rowsrendered' : true,
6100 * @event contextmenu
6101 * The raw contextmenu event for the entire grid.
6102 * @param {Roo.EventObject} e
6104 "contextmenu" : true,
6106 * @event rowcontextmenu
6107 * Fires when a row is right clicked
6108 * @param {Roo.bootstrap.Table} this
6109 * @param {Number} rowIndex
6110 * @param {Roo.EventObject} e
6112 "rowcontextmenu" : true,
6114 * @event cellcontextmenu
6115 * Fires when a cell is right clicked
6116 * @param {Roo.bootstrap.Table} this
6117 * @param {Number} rowIndex
6118 * @param {Number} cellIndex
6119 * @param {Roo.EventObject} e
6121 "cellcontextmenu" : true,
6123 * @event headercontextmenu
6124 * Fires when a header is right clicked
6125 * @param {Roo.bootstrap.Table} this
6126 * @param {Number} columnIndex
6127 * @param {Roo.EventObject} e
6129 "headercontextmenu" : true
6133 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6159 rowSelection : false,
6160 cellSelection : false,
6163 // Roo.Element - the tbody
6165 // Roo.Element - thead element
6168 container: false, // used by gridpanel...
6174 auto_hide_footer : false,
6176 getAutoCreate : function()
6178 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6185 if (this.scrollBody) {
6186 cfg.cls += ' table-body-fixed';
6189 cfg.cls += ' table-striped';
6193 cfg.cls += ' table-hover';
6195 if (this.bordered) {
6196 cfg.cls += ' table-bordered';
6198 if (this.condensed) {
6199 cfg.cls += ' table-condensed';
6201 if (this.responsive) {
6202 cfg.cls += ' table-responsive';
6206 cfg.cls+= ' ' +this.cls;
6209 // this lot should be simplifed...
6222 ].forEach(function(k) {
6230 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6233 if(this.store || this.cm){
6234 if(this.headerShow){
6235 cfg.cn.push(this.renderHeader());
6238 cfg.cn.push(this.renderBody());
6240 if(this.footerShow){
6241 cfg.cn.push(this.renderFooter());
6243 // where does this come from?
6244 //cfg.cls+= ' TableGrid';
6247 return { cn : [ cfg ] };
6250 initEvents : function()
6252 if(!this.store || !this.cm){
6255 if (this.selModel) {
6256 this.selModel.initEvents();
6260 //Roo.log('initEvents with ds!!!!');
6262 this.mainBody = this.el.select('tbody', true).first();
6263 this.mainHead = this.el.select('thead', true).first();
6264 this.mainFoot = this.el.select('tfoot', true).first();
6270 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6271 e.on('click', _this.sort, _this);
6274 this.mainBody.on("click", this.onClick, this);
6275 this.mainBody.on("dblclick", this.onDblClick, this);
6277 // why is this done????? = it breaks dialogs??
6278 //this.parent().el.setStyle('position', 'relative');
6282 this.footer.parentId = this.id;
6283 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6286 this.el.select('tfoot tr td').first().addClass('hide');
6291 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6294 this.store.on('load', this.onLoad, this);
6295 this.store.on('beforeload', this.onBeforeLoad, this);
6296 this.store.on('update', this.onUpdate, this);
6297 this.store.on('add', this.onAdd, this);
6298 this.store.on("clear", this.clear, this);
6300 this.el.on("contextmenu", this.onContextMenu, this);
6302 this.mainBody.on('scroll', this.onBodyScroll, this);
6304 this.cm.on("headerchange", this.onHeaderChange, this);
6306 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6310 onContextMenu : function(e, t)
6312 this.processEvent("contextmenu", e);
6315 processEvent : function(name, e)
6317 if (name != 'touchstart' ) {
6318 this.fireEvent(name, e);
6321 var t = e.getTarget();
6323 var cell = Roo.get(t);
6329 if(cell.findParent('tfoot', false, true)){
6333 if(cell.findParent('thead', false, true)){
6335 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6336 cell = Roo.get(t).findParent('th', false, true);
6338 Roo.log("failed to find th in thead?");
6339 Roo.log(e.getTarget());
6344 var cellIndex = cell.dom.cellIndex;
6346 var ename = name == 'touchstart' ? 'click' : name;
6347 this.fireEvent("header" + ename, this, cellIndex, e);
6352 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6353 cell = Roo.get(t).findParent('td', false, true);
6355 Roo.log("failed to find th in tbody?");
6356 Roo.log(e.getTarget());
6361 var row = cell.findParent('tr', false, true);
6362 var cellIndex = cell.dom.cellIndex;
6363 var rowIndex = row.dom.rowIndex - 1;
6367 this.fireEvent("row" + name, this, rowIndex, e);
6371 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6377 onMouseover : function(e, el)
6379 var cell = Roo.get(el);
6385 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6386 cell = cell.findParent('td', false, true);
6389 var row = cell.findParent('tr', false, true);
6390 var cellIndex = cell.dom.cellIndex;
6391 var rowIndex = row.dom.rowIndex - 1; // start from 0
6393 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6397 onMouseout : function(e, el)
6399 var cell = Roo.get(el);
6405 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6406 cell = cell.findParent('td', false, true);
6409 var row = cell.findParent('tr', false, true);
6410 var cellIndex = cell.dom.cellIndex;
6411 var rowIndex = row.dom.rowIndex - 1; // start from 0
6413 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6417 onClick : function(e, el)
6419 var cell = Roo.get(el);
6421 if(!cell || (!this.cellSelection && !this.rowSelection)){
6425 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6426 cell = cell.findParent('td', false, true);
6429 if(!cell || typeof(cell) == 'undefined'){
6433 var row = cell.findParent('tr', false, true);
6435 if(!row || typeof(row) == 'undefined'){
6439 var cellIndex = cell.dom.cellIndex;
6440 var rowIndex = this.getRowIndex(row);
6442 // why??? - should these not be based on SelectionModel?
6443 if(this.cellSelection){
6444 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6447 if(this.rowSelection){
6448 this.fireEvent('rowclick', this, row, rowIndex, e);
6454 onDblClick : function(e,el)
6456 var cell = Roo.get(el);
6458 if(!cell || (!this.cellSelection && !this.rowSelection)){
6462 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6463 cell = cell.findParent('td', false, true);
6466 if(!cell || typeof(cell) == 'undefined'){
6470 var row = cell.findParent('tr', false, true);
6472 if(!row || typeof(row) == 'undefined'){
6476 var cellIndex = cell.dom.cellIndex;
6477 var rowIndex = this.getRowIndex(row);
6479 if(this.cellSelection){
6480 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6483 if(this.rowSelection){
6484 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6488 sort : function(e,el)
6490 var col = Roo.get(el);
6492 if(!col.hasClass('sortable')){
6496 var sort = col.attr('sort');
6499 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6503 this.store.sortInfo = {field : sort, direction : dir};
6506 Roo.log("calling footer first");
6507 this.footer.onClick('first');
6510 this.store.load({ params : { start : 0 } });
6514 renderHeader : function()
6522 this.totalWidth = 0;
6524 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6526 var config = cm.config[i];
6530 cls : 'x-hcol-' + i,
6532 html: cm.getColumnHeader(i)
6537 if(typeof(config.sortable) != 'undefined' && config.sortable){
6539 c.html = '<i class="glyphicon"></i>' + c.html;
6542 if(typeof(config.lgHeader) != 'undefined'){
6543 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6546 if(typeof(config.mdHeader) != 'undefined'){
6547 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6550 if(typeof(config.smHeader) != 'undefined'){
6551 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6554 if(typeof(config.xsHeader) != 'undefined'){
6555 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6562 if(typeof(config.tooltip) != 'undefined'){
6563 c.tooltip = config.tooltip;
6566 if(typeof(config.colspan) != 'undefined'){
6567 c.colspan = config.colspan;
6570 if(typeof(config.hidden) != 'undefined' && config.hidden){
6571 c.style += ' display:none;';
6574 if(typeof(config.dataIndex) != 'undefined'){
6575 c.sort = config.dataIndex;
6580 if(typeof(config.align) != 'undefined' && config.align.length){
6581 c.style += ' text-align:' + config.align + ';';
6584 if(typeof(config.width) != 'undefined'){
6585 c.style += ' width:' + config.width + 'px;';
6586 this.totalWidth += config.width;
6588 this.totalWidth += 100; // assume minimum of 100 per column?
6591 if(typeof(config.cls) != 'undefined'){
6592 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6595 ['xs','sm','md','lg'].map(function(size){
6597 if(typeof(config[size]) == 'undefined'){
6601 if (!config[size]) { // 0 = hidden
6602 c.cls += ' hidden-' + size;
6606 c.cls += ' col-' + size + '-' + config[size];
6616 renderBody : function()
6626 colspan : this.cm.getColumnCount()
6636 renderFooter : function()
6646 colspan : this.cm.getColumnCount()
6660 // Roo.log('ds onload');
6665 var ds = this.store;
6667 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6668 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6669 if (_this.store.sortInfo) {
6671 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6672 e.select('i', true).addClass(['glyphicon-arrow-up']);
6675 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6676 e.select('i', true).addClass(['glyphicon-arrow-down']);
6681 var tbody = this.mainBody;
6683 if(ds.getCount() > 0){
6684 ds.data.each(function(d,rowIndex){
6685 var row = this.renderRow(cm, ds, rowIndex);
6687 tbody.createChild(row);
6691 if(row.cellObjects.length){
6692 Roo.each(row.cellObjects, function(r){
6693 _this.renderCellObject(r);
6700 var tfoot = this.el.select('tfoot', true).first();
6702 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6704 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6706 var total = this.ds.getTotalCount();
6708 if(this.footer.pageSize < total){
6709 this.mainFoot.show();
6713 Roo.each(this.el.select('tbody td', true).elements, function(e){
6714 e.on('mouseover', _this.onMouseover, _this);
6717 Roo.each(this.el.select('tbody td', true).elements, function(e){
6718 e.on('mouseout', _this.onMouseout, _this);
6720 this.fireEvent('rowsrendered', this);
6726 onUpdate : function(ds,record)
6728 this.refreshRow(record);
6732 onRemove : function(ds, record, index, isUpdate){
6733 if(isUpdate !== true){
6734 this.fireEvent("beforerowremoved", this, index, record);
6736 var bt = this.mainBody.dom;
6738 var rows = this.el.select('tbody > tr', true).elements;
6740 if(typeof(rows[index]) != 'undefined'){
6741 bt.removeChild(rows[index].dom);
6744 // if(bt.rows[index]){
6745 // bt.removeChild(bt.rows[index]);
6748 if(isUpdate !== true){
6749 //this.stripeRows(index);
6750 //this.syncRowHeights(index, index);
6752 this.fireEvent("rowremoved", this, index, record);
6756 onAdd : function(ds, records, rowIndex)
6758 //Roo.log('on Add called');
6759 // - note this does not handle multiple adding very well..
6760 var bt = this.mainBody.dom;
6761 for (var i =0 ; i < records.length;i++) {
6762 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6763 //Roo.log(records[i]);
6764 //Roo.log(this.store.getAt(rowIndex+i));
6765 this.insertRow(this.store, rowIndex + i, false);
6772 refreshRow : function(record){
6773 var ds = this.store, index;
6774 if(typeof record == 'number'){
6776 record = ds.getAt(index);
6778 index = ds.indexOf(record);
6780 this.insertRow(ds, index, true);
6782 this.onRemove(ds, record, index+1, true);
6784 //this.syncRowHeights(index, index);
6786 this.fireEvent("rowupdated", this, index, record);
6789 insertRow : function(dm, rowIndex, isUpdate){
6792 this.fireEvent("beforerowsinserted", this, rowIndex);
6794 //var s = this.getScrollState();
6795 var row = this.renderRow(this.cm, this.store, rowIndex);
6796 // insert before rowIndex..
6797 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6801 if(row.cellObjects.length){
6802 Roo.each(row.cellObjects, function(r){
6803 _this.renderCellObject(r);
6808 this.fireEvent("rowsinserted", this, rowIndex);
6809 //this.syncRowHeights(firstRow, lastRow);
6810 //this.stripeRows(firstRow);
6817 getRowDom : function(rowIndex)
6819 var rows = this.el.select('tbody > tr', true).elements;
6821 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6824 // returns the object tree for a tr..
6827 renderRow : function(cm, ds, rowIndex)
6829 var d = ds.getAt(rowIndex);
6833 cls : 'x-row-' + rowIndex,
6837 var cellObjects = [];
6839 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6840 var config = cm.config[i];
6842 var renderer = cm.getRenderer(i);
6846 if(typeof(renderer) !== 'undefined'){
6847 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6849 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6850 // and are rendered into the cells after the row is rendered - using the id for the element.
6852 if(typeof(value) === 'object'){
6862 rowIndex : rowIndex,
6867 this.fireEvent('rowclass', this, rowcfg);
6871 cls : rowcfg.rowClass + ' x-col-' + i,
6873 html: (typeof(value) === 'object') ? '' : value
6880 if(typeof(config.colspan) != 'undefined'){
6881 td.colspan = config.colspan;
6884 if(typeof(config.hidden) != 'undefined' && config.hidden){
6885 td.style += ' display:none;';
6888 if(typeof(config.align) != 'undefined' && config.align.length){
6889 td.style += ' text-align:' + config.align + ';';
6891 if(typeof(config.valign) != 'undefined' && config.valign.length){
6892 td.style += ' vertical-align:' + config.valign + ';';
6895 if(typeof(config.width) != 'undefined'){
6896 td.style += ' width:' + config.width + 'px;';
6899 if(typeof(config.cursor) != 'undefined'){
6900 td.style += ' cursor:' + config.cursor + ';';
6903 if(typeof(config.cls) != 'undefined'){
6904 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6907 ['xs','sm','md','lg'].map(function(size){
6909 if(typeof(config[size]) == 'undefined'){
6913 if (!config[size]) { // 0 = hidden
6914 td.cls += ' hidden-' + size;
6918 td.cls += ' col-' + size + '-' + config[size];
6926 row.cellObjects = cellObjects;
6934 onBeforeLoad : function()
6943 this.el.select('tbody', true).first().dom.innerHTML = '';
6946 * Show or hide a row.
6947 * @param {Number} rowIndex to show or hide
6948 * @param {Boolean} state hide
6950 setRowVisibility : function(rowIndex, state)
6952 var bt = this.mainBody.dom;
6954 var rows = this.el.select('tbody > tr', true).elements;
6956 if(typeof(rows[rowIndex]) == 'undefined'){
6959 rows[rowIndex].dom.style.display = state ? '' : 'none';
6963 getSelectionModel : function(){
6965 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6967 return this.selModel;
6970 * Render the Roo.bootstrap object from renderder
6972 renderCellObject : function(r)
6976 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6978 var t = r.cfg.render(r.container);
6981 Roo.each(r.cfg.cn, function(c){
6983 container: t.getChildContainer(),
6986 _this.renderCellObject(child);
6991 getRowIndex : function(row)
6995 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7006 * Returns the grid's underlying element = used by panel.Grid
7007 * @return {Element} The element
7009 getGridEl : function(){
7013 * Forces a resize - used by panel.Grid
7014 * @return {Element} The element
7016 autoSize : function()
7018 //var ctr = Roo.get(this.container.dom.parentElement);
7019 var ctr = Roo.get(this.el.dom);
7021 var thd = this.getGridEl().select('thead',true).first();
7022 var tbd = this.getGridEl().select('tbody', true).first();
7023 var tfd = this.getGridEl().select('tfoot', true).first();
7025 var cw = ctr.getWidth();
7029 tbd.setSize(ctr.getWidth(),
7030 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7032 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7035 cw = Math.max(cw, this.totalWidth);
7036 this.getGridEl().select('tr',true).setWidth(cw);
7037 // resize 'expandable coloumn?
7039 return; // we doe not have a view in this design..
7042 onBodyScroll: function()
7044 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7046 this.mainHead.setStyle({
7047 'position' : 'relative',
7048 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7054 var scrollHeight = this.mainBody.dom.scrollHeight;
7056 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7058 var height = this.mainBody.getHeight();
7060 if(scrollHeight - height == scrollTop) {
7062 var total = this.ds.getTotalCount();
7064 if(this.footer.cursor + this.footer.pageSize < total){
7066 this.footer.ds.load({
7068 start : this.footer.cursor + this.footer.pageSize,
7069 limit : this.footer.pageSize
7079 onHeaderChange : function()
7081 var header = this.renderHeader();
7082 var table = this.el.select('table', true).first();
7084 this.mainHead.remove();
7085 this.mainHead = table.createChild(header, this.mainBody, false);
7088 onHiddenChange : function(colModel, colIndex, hidden)
7090 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7091 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7093 this.CSS.updateRule(thSelector, "display", "");
7094 this.CSS.updateRule(tdSelector, "display", "");
7097 this.CSS.updateRule(thSelector, "display", "none");
7098 this.CSS.updateRule(tdSelector, "display", "none");
7101 this.onHeaderChange();
7118 * @class Roo.bootstrap.TableCell
7119 * @extends Roo.bootstrap.Component
7120 * Bootstrap TableCell class
7121 * @cfg {String} html cell contain text
7122 * @cfg {String} cls cell class
7123 * @cfg {String} tag cell tag (td|th) default td
7124 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7125 * @cfg {String} align Aligns the content in a cell
7126 * @cfg {String} axis Categorizes cells
7127 * @cfg {String} bgcolor Specifies the background color of a cell
7128 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7129 * @cfg {Number} colspan Specifies the number of columns a cell should span
7130 * @cfg {String} headers Specifies one or more header cells a cell is related to
7131 * @cfg {Number} height Sets the height of a cell
7132 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7133 * @cfg {Number} rowspan Sets the number of rows a cell should span
7134 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7135 * @cfg {String} valign Vertical aligns the content in a cell
7136 * @cfg {Number} width Specifies the width of a cell
7139 * Create a new TableCell
7140 * @param {Object} config The config object
7143 Roo.bootstrap.TableCell = function(config){
7144 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7147 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7167 getAutoCreate : function(){
7168 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7188 cfg.align=this.align
7194 cfg.bgcolor=this.bgcolor
7197 cfg.charoff=this.charoff
7200 cfg.colspan=this.colspan
7203 cfg.headers=this.headers
7206 cfg.height=this.height
7209 cfg.nowrap=this.nowrap
7212 cfg.rowspan=this.rowspan
7215 cfg.scope=this.scope
7218 cfg.valign=this.valign
7221 cfg.width=this.width
7240 * @class Roo.bootstrap.TableRow
7241 * @extends Roo.bootstrap.Component
7242 * Bootstrap TableRow class
7243 * @cfg {String} cls row class
7244 * @cfg {String} align Aligns the content in a table row
7245 * @cfg {String} bgcolor Specifies a background color for a table row
7246 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7247 * @cfg {String} valign Vertical aligns the content in a table row
7250 * Create a new TableRow
7251 * @param {Object} config The config object
7254 Roo.bootstrap.TableRow = function(config){
7255 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7258 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7266 getAutoCreate : function(){
7267 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7277 cfg.align = this.align;
7280 cfg.bgcolor = this.bgcolor;
7283 cfg.charoff = this.charoff;
7286 cfg.valign = this.valign;
7304 * @class Roo.bootstrap.TableBody
7305 * @extends Roo.bootstrap.Component
7306 * Bootstrap TableBody class
7307 * @cfg {String} cls element class
7308 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7309 * @cfg {String} align Aligns the content inside the element
7310 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7311 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7314 * Create a new TableBody
7315 * @param {Object} config The config object
7318 Roo.bootstrap.TableBody = function(config){
7319 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7322 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7330 getAutoCreate : function(){
7331 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7345 cfg.align = this.align;
7348 cfg.charoff = this.charoff;
7351 cfg.valign = this.valign;
7358 // initEvents : function()
7365 // this.store = Roo.factory(this.store, Roo.data);
7366 // this.store.on('load', this.onLoad, this);
7368 // this.store.load();
7372 // onLoad: function ()
7374 // this.fireEvent('load', this);
7384 * Ext JS Library 1.1.1
7385 * Copyright(c) 2006-2007, Ext JS, LLC.
7387 * Originally Released Under LGPL - original licence link has changed is not relivant.
7390 * <script type="text/javascript">
7393 // as we use this in bootstrap.
7394 Roo.namespace('Roo.form');
7396 * @class Roo.form.Action
7397 * Internal Class used to handle form actions
7399 * @param {Roo.form.BasicForm} el The form element or its id
7400 * @param {Object} config Configuration options
7405 // define the action interface
7406 Roo.form.Action = function(form, options){
7408 this.options = options || {};
7411 * Client Validation Failed
7414 Roo.form.Action.CLIENT_INVALID = 'client';
7416 * Server Validation Failed
7419 Roo.form.Action.SERVER_INVALID = 'server';
7421 * Connect to Server Failed
7424 Roo.form.Action.CONNECT_FAILURE = 'connect';
7426 * Reading Data from Server Failed
7429 Roo.form.Action.LOAD_FAILURE = 'load';
7431 Roo.form.Action.prototype = {
7433 failureType : undefined,
7434 response : undefined,
7438 run : function(options){
7443 success : function(response){
7448 handleResponse : function(response){
7452 // default connection failure
7453 failure : function(response){
7455 this.response = response;
7456 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7457 this.form.afterAction(this, false);
7460 processResponse : function(response){
7461 this.response = response;
7462 if(!response.responseText){
7465 this.result = this.handleResponse(response);
7469 // utility functions used internally
7470 getUrl : function(appendParams){
7471 var url = this.options.url || this.form.url || this.form.el.dom.action;
7473 var p = this.getParams();
7475 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7481 getMethod : function(){
7482 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7485 getParams : function(){
7486 var bp = this.form.baseParams;
7487 var p = this.options.params;
7489 if(typeof p == "object"){
7490 p = Roo.urlEncode(Roo.applyIf(p, bp));
7491 }else if(typeof p == 'string' && bp){
7492 p += '&' + Roo.urlEncode(bp);
7495 p = Roo.urlEncode(bp);
7500 createCallback : function(){
7502 success: this.success,
7503 failure: this.failure,
7505 timeout: (this.form.timeout*1000),
7506 upload: this.form.fileUpload ? this.success : undefined
7511 Roo.form.Action.Submit = function(form, options){
7512 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7515 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7518 haveProgress : false,
7519 uploadComplete : false,
7521 // uploadProgress indicator.
7522 uploadProgress : function()
7524 if (!this.form.progressUrl) {
7528 if (!this.haveProgress) {
7529 Roo.MessageBox.progress("Uploading", "Uploading");
7531 if (this.uploadComplete) {
7532 Roo.MessageBox.hide();
7536 this.haveProgress = true;
7538 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7540 var c = new Roo.data.Connection();
7542 url : this.form.progressUrl,
7547 success : function(req){
7548 //console.log(data);
7552 rdata = Roo.decode(req.responseText)
7554 Roo.log("Invalid data from server..");
7558 if (!rdata || !rdata.success) {
7560 Roo.MessageBox.alert(Roo.encode(rdata));
7563 var data = rdata.data;
7565 if (this.uploadComplete) {
7566 Roo.MessageBox.hide();
7571 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7572 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7575 this.uploadProgress.defer(2000,this);
7578 failure: function(data) {
7579 Roo.log('progress url failed ');
7590 // run get Values on the form, so it syncs any secondary forms.
7591 this.form.getValues();
7593 var o = this.options;
7594 var method = this.getMethod();
7595 var isPost = method == 'POST';
7596 if(o.clientValidation === false || this.form.isValid()){
7598 if (this.form.progressUrl) {
7599 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7600 (new Date() * 1) + '' + Math.random());
7605 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7606 form:this.form.el.dom,
7607 url:this.getUrl(!isPost),
7609 params:isPost ? this.getParams() : null,
7610 isUpload: this.form.fileUpload
7613 this.uploadProgress();
7615 }else if (o.clientValidation !== false){ // client validation failed
7616 this.failureType = Roo.form.Action.CLIENT_INVALID;
7617 this.form.afterAction(this, false);
7621 success : function(response)
7623 this.uploadComplete= true;
7624 if (this.haveProgress) {
7625 Roo.MessageBox.hide();
7629 var result = this.processResponse(response);
7630 if(result === true || result.success){
7631 this.form.afterAction(this, true);
7635 this.form.markInvalid(result.errors);
7636 this.failureType = Roo.form.Action.SERVER_INVALID;
7638 this.form.afterAction(this, false);
7640 failure : function(response)
7642 this.uploadComplete= true;
7643 if (this.haveProgress) {
7644 Roo.MessageBox.hide();
7647 this.response = response;
7648 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7649 this.form.afterAction(this, false);
7652 handleResponse : function(response){
7653 if(this.form.errorReader){
7654 var rs = this.form.errorReader.read(response);
7657 for(var i = 0, len = rs.records.length; i < len; i++) {
7658 var r = rs.records[i];
7662 if(errors.length < 1){
7666 success : rs.success,
7672 ret = Roo.decode(response.responseText);
7676 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7686 Roo.form.Action.Load = function(form, options){
7687 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7688 this.reader = this.form.reader;
7691 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7696 Roo.Ajax.request(Roo.apply(
7697 this.createCallback(), {
7698 method:this.getMethod(),
7699 url:this.getUrl(false),
7700 params:this.getParams()
7704 success : function(response){
7706 var result = this.processResponse(response);
7707 if(result === true || !result.success || !result.data){
7708 this.failureType = Roo.form.Action.LOAD_FAILURE;
7709 this.form.afterAction(this, false);
7712 this.form.clearInvalid();
7713 this.form.setValues(result.data);
7714 this.form.afterAction(this, true);
7717 handleResponse : function(response){
7718 if(this.form.reader){
7719 var rs = this.form.reader.read(response);
7720 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7722 success : rs.success,
7726 return Roo.decode(response.responseText);
7730 Roo.form.Action.ACTION_TYPES = {
7731 'load' : Roo.form.Action.Load,
7732 'submit' : Roo.form.Action.Submit
7741 * @class Roo.bootstrap.Form
7742 * @extends Roo.bootstrap.Component
7743 * Bootstrap Form class
7744 * @cfg {String} method GET | POST (default POST)
7745 * @cfg {String} labelAlign top | left (default top)
7746 * @cfg {String} align left | right - for navbars
7747 * @cfg {Boolean} loadMask load mask when submit (default true)
7752 * @param {Object} config The config object
7756 Roo.bootstrap.Form = function(config){
7758 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7760 Roo.bootstrap.Form.popover.apply();
7764 * @event clientvalidation
7765 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7766 * @param {Form} this
7767 * @param {Boolean} valid true if the form has passed client-side validation
7769 clientvalidation: true,
7771 * @event beforeaction
7772 * Fires before any action is performed. Return false to cancel the action.
7773 * @param {Form} this
7774 * @param {Action} action The action to be performed
7778 * @event actionfailed
7779 * Fires when an action fails.
7780 * @param {Form} this
7781 * @param {Action} action The action that failed
7783 actionfailed : true,
7785 * @event actioncomplete
7786 * Fires when an action is completed.
7787 * @param {Form} this
7788 * @param {Action} action The action that completed
7790 actioncomplete : true
7794 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7797 * @cfg {String} method
7798 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7803 * The URL to use for form actions if one isn't supplied in the action options.
7806 * @cfg {Boolean} fileUpload
7807 * Set to true if this form is a file upload.
7811 * @cfg {Object} baseParams
7812 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7816 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7820 * @cfg {Sting} align (left|right) for navbar forms
7825 activeAction : null,
7828 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7829 * element by passing it or its id or mask the form itself by passing in true.
7832 waitMsgTarget : false,
7837 * @cfg {Boolean} errorMask (true|false) default false
7842 * @cfg {Number} maskOffset Default 100
7847 * @cfg {Boolean} maskBody
7851 getAutoCreate : function(){
7855 method : this.method || 'POST',
7856 id : this.id || Roo.id(),
7859 if (this.parent().xtype.match(/^Nav/)) {
7860 cfg.cls = 'navbar-form navbar-' + this.align;
7864 if (this.labelAlign == 'left' ) {
7865 cfg.cls += ' form-horizontal';
7871 initEvents : function()
7873 this.el.on('submit', this.onSubmit, this);
7874 // this was added as random key presses on the form where triggering form submit.
7875 this.el.on('keypress', function(e) {
7876 if (e.getCharCode() != 13) {
7879 // we might need to allow it for textareas.. and some other items.
7880 // check e.getTarget().
7882 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7886 Roo.log("keypress blocked");
7894 onSubmit : function(e){
7899 * Returns true if client-side validation on the form is successful.
7902 isValid : function(){
7903 var items = this.getItems();
7907 items.each(function(f){
7913 Roo.log('invalid field: ' + f.name);
7917 if(!target && f.el.isVisible(true)){
7923 if(this.errorMask && !valid){
7924 Roo.bootstrap.Form.popover.mask(this, target);
7931 * Returns true if any fields in this form have changed since their original load.
7934 isDirty : function(){
7936 var items = this.getItems();
7937 items.each(function(f){
7947 * Performs a predefined action (submit or load) or custom actions you define on this form.
7948 * @param {String} actionName The name of the action type
7949 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7950 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7951 * accept other config options):
7953 Property Type Description
7954 ---------------- --------------- ----------------------------------------------------------------------------------
7955 url String The url for the action (defaults to the form's url)
7956 method String The form method to use (defaults to the form's method, or POST if not defined)
7957 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7958 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7959 validate the form on the client (defaults to false)
7961 * @return {BasicForm} this
7963 doAction : function(action, options){
7964 if(typeof action == 'string'){
7965 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7967 if(this.fireEvent('beforeaction', this, action) !== false){
7968 this.beforeAction(action);
7969 action.run.defer(100, action);
7975 beforeAction : function(action){
7976 var o = action.options;
7981 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7983 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7986 // not really supported yet.. ??
7988 //if(this.waitMsgTarget === true){
7989 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7990 //}else if(this.waitMsgTarget){
7991 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7992 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7994 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8000 afterAction : function(action, success){
8001 this.activeAction = null;
8002 var o = action.options;
8007 Roo.get(document.body).unmask();
8013 //if(this.waitMsgTarget === true){
8014 // this.el.unmask();
8015 //}else if(this.waitMsgTarget){
8016 // this.waitMsgTarget.unmask();
8018 // Roo.MessageBox.updateProgress(1);
8019 // Roo.MessageBox.hide();
8026 Roo.callback(o.success, o.scope, [this, action]);
8027 this.fireEvent('actioncomplete', this, action);
8031 // failure condition..
8032 // we have a scenario where updates need confirming.
8033 // eg. if a locking scenario exists..
8034 // we look for { errors : { needs_confirm : true }} in the response.
8036 (typeof(action.result) != 'undefined') &&
8037 (typeof(action.result.errors) != 'undefined') &&
8038 (typeof(action.result.errors.needs_confirm) != 'undefined')
8041 Roo.log("not supported yet");
8044 Roo.MessageBox.confirm(
8045 "Change requires confirmation",
8046 action.result.errorMsg,
8051 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8061 Roo.callback(o.failure, o.scope, [this, action]);
8062 // show an error message if no failed handler is set..
8063 if (!this.hasListener('actionfailed')) {
8064 Roo.log("need to add dialog support");
8066 Roo.MessageBox.alert("Error",
8067 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8068 action.result.errorMsg :
8069 "Saving Failed, please check your entries or try again"
8074 this.fireEvent('actionfailed', this, action);
8079 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8080 * @param {String} id The value to search for
8083 findField : function(id){
8084 var items = this.getItems();
8085 var field = items.get(id);
8087 items.each(function(f){
8088 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8095 return field || null;
8098 * Mark fields in this form invalid in bulk.
8099 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8100 * @return {BasicForm} this
8102 markInvalid : function(errors){
8103 if(errors instanceof Array){
8104 for(var i = 0, len = errors.length; i < len; i++){
8105 var fieldError = errors[i];
8106 var f = this.findField(fieldError.id);
8108 f.markInvalid(fieldError.msg);
8114 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8115 field.markInvalid(errors[id]);
8119 //Roo.each(this.childForms || [], function (f) {
8120 // f.markInvalid(errors);
8127 * Set values for fields in this form in bulk.
8128 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8129 * @return {BasicForm} this
8131 setValues : function(values){
8132 if(values instanceof Array){ // array of objects
8133 for(var i = 0, len = values.length; i < len; i++){
8135 var f = this.findField(v.id);
8137 f.setValue(v.value);
8138 if(this.trackResetOnLoad){
8139 f.originalValue = f.getValue();
8143 }else{ // object hash
8146 if(typeof values[id] != 'function' && (field = this.findField(id))){
8148 if (field.setFromData &&
8150 field.displayField &&
8151 // combos' with local stores can
8152 // be queried via setValue()
8153 // to set their value..
8154 (field.store && !field.store.isLocal)
8158 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8159 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8160 field.setFromData(sd);
8162 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8164 field.setFromData(values);
8167 field.setValue(values[id]);
8171 if(this.trackResetOnLoad){
8172 field.originalValue = field.getValue();
8178 //Roo.each(this.childForms || [], function (f) {
8179 // f.setValues(values);
8186 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8187 * they are returned as an array.
8188 * @param {Boolean} asString
8191 getValues : function(asString){
8192 //if (this.childForms) {
8193 // copy values from the child forms
8194 // Roo.each(this.childForms, function (f) {
8195 // this.setValues(f.getValues());
8201 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8202 if(asString === true){
8205 return Roo.urlDecode(fs);
8209 * Returns the fields in this form as an object with key/value pairs.
8210 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8213 getFieldValues : function(with_hidden)
8215 var items = this.getItems();
8217 items.each(function(f){
8223 var v = f.getValue();
8225 if (f.inputType =='radio') {
8226 if (typeof(ret[f.getName()]) == 'undefined') {
8227 ret[f.getName()] = ''; // empty..
8230 if (!f.el.dom.checked) {
8238 if(f.xtype == 'MoneyField'){
8239 ret[f.currencyName] = f.getCurrency();
8242 // not sure if this supported any more..
8243 if ((typeof(v) == 'object') && f.getRawValue) {
8244 v = f.getRawValue() ; // dates..
8246 // combo boxes where name != hiddenName...
8247 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8248 ret[f.name] = f.getRawValue();
8250 ret[f.getName()] = v;
8257 * Clears all invalid messages in this form.
8258 * @return {BasicForm} this
8260 clearInvalid : function(){
8261 var items = this.getItems();
8263 items.each(function(f){
8272 * @return {BasicForm} this
8275 var items = this.getItems();
8276 items.each(function(f){
8280 Roo.each(this.childForms || [], function (f) {
8288 getItems : function()
8290 var r=new Roo.util.MixedCollection(false, function(o){
8291 return o.id || (o.id = Roo.id());
8293 var iter = function(el) {
8300 Roo.each(el.items,function(e) {
8309 hideFields : function(items)
8311 Roo.each(items, function(i){
8313 var f = this.findField(i);
8319 if(f.xtype == 'DateField'){
8320 f.setVisible(false);
8329 showFields : function(items)
8331 Roo.each(items, function(i){
8333 var f = this.findField(i);
8339 if(f.xtype == 'DateField'){
8351 Roo.apply(Roo.bootstrap.Form, {
8378 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8379 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8380 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8381 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8384 this.maskEl.top.enableDisplayMode("block");
8385 this.maskEl.left.enableDisplayMode("block");
8386 this.maskEl.bottom.enableDisplayMode("block");
8387 this.maskEl.right.enableDisplayMode("block");
8389 this.toolTip = new Roo.bootstrap.Tooltip({
8390 cls : 'roo-form-error-popover',
8392 'left' : ['r-l', [-2,0], 'right'],
8393 'right' : ['l-r', [2,0], 'left'],
8394 'bottom' : ['tl-bl', [0,2], 'top'],
8395 'top' : [ 'bl-tl', [0,-2], 'bottom']
8399 this.toolTip.render(Roo.get(document.body));
8401 this.toolTip.el.enableDisplayMode("block");
8403 Roo.get(document.body).on('click', function(){
8407 Roo.get(document.body).on('touchstart', function(){
8411 this.isApplied = true
8414 mask : function(form, target)
8418 this.target = target;
8420 if(!this.form.errorMask || !target.el){
8424 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8426 Roo.log(scrollable);
8428 var ot = this.target.el.calcOffsetsTo(scrollable);
8430 var scrollTo = ot[1] - this.form.maskOffset;
8432 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8434 scrollable.scrollTo('top', scrollTo);
8436 var box = this.target.el.getBox();
8438 var zIndex = Roo.bootstrap.Modal.zIndex++;
8441 this.maskEl.top.setStyle('position', 'absolute');
8442 this.maskEl.top.setStyle('z-index', zIndex);
8443 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8444 this.maskEl.top.setLeft(0);
8445 this.maskEl.top.setTop(0);
8446 this.maskEl.top.show();
8448 this.maskEl.left.setStyle('position', 'absolute');
8449 this.maskEl.left.setStyle('z-index', zIndex);
8450 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8451 this.maskEl.left.setLeft(0);
8452 this.maskEl.left.setTop(box.y - this.padding);
8453 this.maskEl.left.show();
8455 this.maskEl.bottom.setStyle('position', 'absolute');
8456 this.maskEl.bottom.setStyle('z-index', zIndex);
8457 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8458 this.maskEl.bottom.setLeft(0);
8459 this.maskEl.bottom.setTop(box.bottom + this.padding);
8460 this.maskEl.bottom.show();
8462 this.maskEl.right.setStyle('position', 'absolute');
8463 this.maskEl.right.setStyle('z-index', zIndex);
8464 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8465 this.maskEl.right.setLeft(box.right + this.padding);
8466 this.maskEl.right.setTop(box.y - this.padding);
8467 this.maskEl.right.show();
8469 this.toolTip.bindEl = this.target.el;
8471 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8473 var tip = this.target.blankText;
8475 if(this.target.getValue() !== '' ) {
8477 if (this.target.invalidText.length) {
8478 tip = this.target.invalidText;
8479 } else if (this.target.regexText.length){
8480 tip = this.target.regexText;
8484 this.toolTip.show(tip);
8486 this.intervalID = window.setInterval(function() {
8487 Roo.bootstrap.Form.popover.unmask();
8490 window.onwheel = function(){ return false;};
8492 (function(){ this.isMasked = true; }).defer(500, this);
8498 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8502 this.maskEl.top.setStyle('position', 'absolute');
8503 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8504 this.maskEl.top.hide();
8506 this.maskEl.left.setStyle('position', 'absolute');
8507 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8508 this.maskEl.left.hide();
8510 this.maskEl.bottom.setStyle('position', 'absolute');
8511 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8512 this.maskEl.bottom.hide();
8514 this.maskEl.right.setStyle('position', 'absolute');
8515 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8516 this.maskEl.right.hide();
8518 this.toolTip.hide();
8520 this.toolTip.el.hide();
8522 window.onwheel = function(){ return true;};
8524 if(this.intervalID){
8525 window.clearInterval(this.intervalID);
8526 this.intervalID = false;
8529 this.isMasked = false;
8539 * Ext JS Library 1.1.1
8540 * Copyright(c) 2006-2007, Ext JS, LLC.
8542 * Originally Released Under LGPL - original licence link has changed is not relivant.
8545 * <script type="text/javascript">
8548 * @class Roo.form.VTypes
8549 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8552 Roo.form.VTypes = function(){
8553 // closure these in so they are only created once.
8554 var alpha = /^[a-zA-Z_]+$/;
8555 var alphanum = /^[a-zA-Z0-9_]+$/;
8556 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8557 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8559 // All these messages and functions are configurable
8562 * The function used to validate email addresses
8563 * @param {String} value The email address
8565 'email' : function(v){
8566 return email.test(v);
8569 * The error text to display when the email validation function returns false
8572 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8574 * The keystroke filter mask to be applied on email input
8577 'emailMask' : /[a-z0-9_\.\-@]/i,
8580 * The function used to validate URLs
8581 * @param {String} value The URL
8583 'url' : function(v){
8587 * The error text to display when the url validation function returns false
8590 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8593 * The function used to validate alpha values
8594 * @param {String} value The value
8596 'alpha' : function(v){
8597 return alpha.test(v);
8600 * The error text to display when the alpha validation function returns false
8603 'alphaText' : 'This field should only contain letters and _',
8605 * The keystroke filter mask to be applied on alpha input
8608 'alphaMask' : /[a-z_]/i,
8611 * The function used to validate alphanumeric values
8612 * @param {String} value The value
8614 'alphanum' : function(v){
8615 return alphanum.test(v);
8618 * The error text to display when the alphanumeric validation function returns false
8621 'alphanumText' : 'This field should only contain letters, numbers and _',
8623 * The keystroke filter mask to be applied on alphanumeric input
8626 'alphanumMask' : /[a-z0-9_]/i
8636 * @class Roo.bootstrap.Input
8637 * @extends Roo.bootstrap.Component
8638 * Bootstrap Input class
8639 * @cfg {Boolean} disabled is it disabled
8640 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8641 * @cfg {String} name name of the input
8642 * @cfg {string} fieldLabel - the label associated
8643 * @cfg {string} placeholder - placeholder to put in text.
8644 * @cfg {string} before - input group add on before
8645 * @cfg {string} after - input group add on after
8646 * @cfg {string} size - (lg|sm) or leave empty..
8647 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8648 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8649 * @cfg {Number} md colspan out of 12 for computer-sized screens
8650 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8651 * @cfg {string} value default value of the input
8652 * @cfg {Number} labelWidth set the width of label
8653 * @cfg {Number} labellg set the width of label (1-12)
8654 * @cfg {Number} labelmd set the width of label (1-12)
8655 * @cfg {Number} labelsm set the width of label (1-12)
8656 * @cfg {Number} labelxs set the width of label (1-12)
8657 * @cfg {String} labelAlign (top|left)
8658 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8659 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8660 * @cfg {String} indicatorpos (left|right) default left
8661 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8662 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8664 * @cfg {String} align (left|center|right) Default left
8665 * @cfg {Boolean} forceFeedback (true|false) Default false
8668 * Create a new Input
8669 * @param {Object} config The config object
8672 Roo.bootstrap.Input = function(config){
8674 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8679 * Fires when this field receives input focus.
8680 * @param {Roo.form.Field} this
8685 * Fires when this field loses input focus.
8686 * @param {Roo.form.Field} this
8691 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8692 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8693 * @param {Roo.form.Field} this
8694 * @param {Roo.EventObject} e The event object
8699 * Fires just before the field blurs if the field value has changed.
8700 * @param {Roo.form.Field} this
8701 * @param {Mixed} newValue The new value
8702 * @param {Mixed} oldValue The original value
8707 * Fires after the field has been marked as invalid.
8708 * @param {Roo.form.Field} this
8709 * @param {String} msg The validation message
8714 * Fires after the field has been validated with no errors.
8715 * @param {Roo.form.Field} this
8720 * Fires after the key up
8721 * @param {Roo.form.Field} this
8722 * @param {Roo.EventObject} e The event Object
8728 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8730 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8731 automatic validation (defaults to "keyup").
8733 validationEvent : "keyup",
8735 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8737 validateOnBlur : true,
8739 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8741 validationDelay : 250,
8743 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8745 focusClass : "x-form-focus", // not needed???
8749 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8751 invalidClass : "has-warning",
8754 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8756 validClass : "has-success",
8759 * @cfg {Boolean} hasFeedback (true|false) default true
8764 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8766 invalidFeedbackClass : "glyphicon-warning-sign",
8769 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8771 validFeedbackClass : "glyphicon-ok",
8774 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8776 selectOnFocus : false,
8779 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8783 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8788 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8790 disableKeyFilter : false,
8793 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8797 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8801 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8803 blankText : "Please complete this mandatory field",
8806 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8810 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8812 maxLength : Number.MAX_VALUE,
8814 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8816 minLengthText : "The minimum length for this field is {0}",
8818 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8820 maxLengthText : "The maximum length for this field is {0}",
8824 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8825 * If available, this function will be called only after the basic validators all return true, and will be passed the
8826 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8830 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8831 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8832 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8836 * @cfg {String} regexText -- Depricated - use Invalid Text
8841 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8847 autocomplete: false,
8866 formatedValue : false,
8867 forceFeedback : false,
8869 indicatorpos : 'left',
8879 parentLabelAlign : function()
8882 while (parent.parent()) {
8883 parent = parent.parent();
8884 if (typeof(parent.labelAlign) !='undefined') {
8885 return parent.labelAlign;
8892 getAutoCreate : function()
8894 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8900 if(this.inputType != 'hidden'){
8901 cfg.cls = 'form-group' //input-group
8907 type : this.inputType,
8909 cls : 'form-control',
8910 placeholder : this.placeholder || '',
8911 autocomplete : this.autocomplete || 'new-password'
8914 if(this.capture.length){
8915 input.capture = this.capture;
8918 if(this.accept.length){
8919 input.accept = this.accept + "/*";
8923 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8926 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8927 input.maxLength = this.maxLength;
8930 if (this.disabled) {
8931 input.disabled=true;
8934 if (this.readOnly) {
8935 input.readonly=true;
8939 input.name = this.name;
8943 input.cls += ' input-' + this.size;
8947 ['xs','sm','md','lg'].map(function(size){
8948 if (settings[size]) {
8949 cfg.cls += ' col-' + size + '-' + settings[size];
8953 var inputblock = input;
8957 cls: 'glyphicon form-control-feedback'
8960 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8963 cls : 'has-feedback',
8971 if (this.before || this.after) {
8974 cls : 'input-group',
8978 if (this.before && typeof(this.before) == 'string') {
8980 inputblock.cn.push({
8982 cls : 'roo-input-before input-group-addon',
8986 if (this.before && typeof(this.before) == 'object') {
8987 this.before = Roo.factory(this.before);
8989 inputblock.cn.push({
8991 cls : 'roo-input-before input-group-' +
8992 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8996 inputblock.cn.push(input);
8998 if (this.after && typeof(this.after) == 'string') {
8999 inputblock.cn.push({
9001 cls : 'roo-input-after input-group-addon',
9005 if (this.after && typeof(this.after) == 'object') {
9006 this.after = Roo.factory(this.after);
9008 inputblock.cn.push({
9010 cls : 'roo-input-after input-group-' +
9011 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9015 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9016 inputblock.cls += ' has-feedback';
9017 inputblock.cn.push(feedback);
9021 if (align ==='left' && this.fieldLabel.length) {
9023 cfg.cls += ' roo-form-group-label-left';
9028 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9029 tooltip : 'This field is required'
9034 cls : 'control-label',
9035 html : this.fieldLabel
9046 var labelCfg = cfg.cn[1];
9047 var contentCfg = cfg.cn[2];
9049 if(this.indicatorpos == 'right'){
9054 cls : 'control-label',
9058 html : this.fieldLabel
9062 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9063 tooltip : 'This field is required'
9076 labelCfg = cfg.cn[0];
9077 contentCfg = cfg.cn[1];
9081 if(this.labelWidth > 12){
9082 labelCfg.style = "width: " + this.labelWidth + 'px';
9085 if(this.labelWidth < 13 && this.labelmd == 0){
9086 this.labelmd = this.labelWidth;
9089 if(this.labellg > 0){
9090 labelCfg.cls += ' col-lg-' + this.labellg;
9091 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9094 if(this.labelmd > 0){
9095 labelCfg.cls += ' col-md-' + this.labelmd;
9096 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9099 if(this.labelsm > 0){
9100 labelCfg.cls += ' col-sm-' + this.labelsm;
9101 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9104 if(this.labelxs > 0){
9105 labelCfg.cls += ' col-xs-' + this.labelxs;
9106 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9110 } else if ( this.fieldLabel.length) {
9115 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9116 tooltip : 'This field is required'
9120 //cls : 'input-group-addon',
9121 html : this.fieldLabel
9129 if(this.indicatorpos == 'right'){
9134 //cls : 'input-group-addon',
9135 html : this.fieldLabel
9140 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9141 tooltip : 'This field is required'
9161 if (this.parentType === 'Navbar' && this.parent().bar) {
9162 cfg.cls += ' navbar-form';
9165 if (this.parentType === 'NavGroup') {
9166 cfg.cls += ' navbar-form';
9174 * return the real input element.
9176 inputEl: function ()
9178 return this.el.select('input.form-control',true).first();
9181 tooltipEl : function()
9183 return this.inputEl();
9186 indicatorEl : function()
9188 var indicator = this.el.select('i.roo-required-indicator',true).first();
9198 setDisabled : function(v)
9200 var i = this.inputEl().dom;
9202 i.removeAttribute('disabled');
9206 i.setAttribute('disabled','true');
9208 initEvents : function()
9211 this.inputEl().on("keydown" , this.fireKey, this);
9212 this.inputEl().on("focus", this.onFocus, this);
9213 this.inputEl().on("blur", this.onBlur, this);
9215 this.inputEl().relayEvent('keyup', this);
9217 this.indicator = this.indicatorEl();
9220 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9223 // reference to original value for reset
9224 this.originalValue = this.getValue();
9225 //Roo.form.TextField.superclass.initEvents.call(this);
9226 if(this.validationEvent == 'keyup'){
9227 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9228 this.inputEl().on('keyup', this.filterValidation, this);
9230 else if(this.validationEvent !== false){
9231 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9234 if(this.selectOnFocus){
9235 this.on("focus", this.preFocus, this);
9238 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9239 this.inputEl().on("keypress", this.filterKeys, this);
9241 this.inputEl().relayEvent('keypress', this);
9244 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9245 this.el.on("click", this.autoSize, this);
9248 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9249 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9252 if (typeof(this.before) == 'object') {
9253 this.before.render(this.el.select('.roo-input-before',true).first());
9255 if (typeof(this.after) == 'object') {
9256 this.after.render(this.el.select('.roo-input-after',true).first());
9259 this.inputEl().on('change', this.onChange, this);
9262 filterValidation : function(e){
9263 if(!e.isNavKeyPress()){
9264 this.validationTask.delay(this.validationDelay);
9268 * Validates the field value
9269 * @return {Boolean} True if the value is valid, else false
9271 validate : function(){
9272 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9273 if(this.disabled || this.validateValue(this.getRawValue())){
9284 * Validates a value according to the field's validation rules and marks the field as invalid
9285 * if the validation fails
9286 * @param {Mixed} value The value to validate
9287 * @return {Boolean} True if the value is valid, else false
9289 validateValue : function(value)
9291 if(this.getVisibilityEl().hasClass('hidden')){
9295 if(value.length < 1) { // if it's blank
9296 if(this.allowBlank){
9302 if(value.length < this.minLength){
9305 if(value.length > this.maxLength){
9309 var vt = Roo.form.VTypes;
9310 if(!vt[this.vtype](value, this)){
9314 if(typeof this.validator == "function"){
9315 var msg = this.validator(value);
9319 if (typeof(msg) == 'string') {
9320 this.invalidText = msg;
9324 if(this.regex && !this.regex.test(value)){
9332 fireKey : function(e){
9333 //Roo.log('field ' + e.getKey());
9334 if(e.isNavKeyPress()){
9335 this.fireEvent("specialkey", this, e);
9338 focus : function (selectText){
9340 this.inputEl().focus();
9341 if(selectText === true){
9342 this.inputEl().dom.select();
9348 onFocus : function(){
9349 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9350 // this.el.addClass(this.focusClass);
9353 this.hasFocus = true;
9354 this.startValue = this.getValue();
9355 this.fireEvent("focus", this);
9359 beforeBlur : Roo.emptyFn,
9363 onBlur : function(){
9365 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9366 //this.el.removeClass(this.focusClass);
9368 this.hasFocus = false;
9369 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9372 var v = this.getValue();
9373 if(String(v) !== String(this.startValue)){
9374 this.fireEvent('change', this, v, this.startValue);
9376 this.fireEvent("blur", this);
9379 onChange : function(e)
9381 var v = this.getValue();
9382 if(String(v) !== String(this.startValue)){
9383 this.fireEvent('change', this, v, this.startValue);
9389 * Resets the current field value to the originally loaded value and clears any validation messages
9392 this.setValue(this.originalValue);
9396 * Returns the name of the field
9397 * @return {Mixed} name The name field
9399 getName: function(){
9403 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9404 * @return {Mixed} value The field value
9406 getValue : function(){
9408 var v = this.inputEl().getValue();
9413 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9414 * @return {Mixed} value The field value
9416 getRawValue : function(){
9417 var v = this.inputEl().getValue();
9423 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9424 * @param {Mixed} value The value to set
9426 setRawValue : function(v){
9427 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9430 selectText : function(start, end){
9431 var v = this.getRawValue();
9433 start = start === undefined ? 0 : start;
9434 end = end === undefined ? v.length : end;
9435 var d = this.inputEl().dom;
9436 if(d.setSelectionRange){
9437 d.setSelectionRange(start, end);
9438 }else if(d.createTextRange){
9439 var range = d.createTextRange();
9440 range.moveStart("character", start);
9441 range.moveEnd("character", v.length-end);
9448 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9449 * @param {Mixed} value The value to set
9451 setValue : function(v){
9454 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9460 processValue : function(value){
9461 if(this.stripCharsRe){
9462 var newValue = value.replace(this.stripCharsRe, '');
9463 if(newValue !== value){
9464 this.setRawValue(newValue);
9471 preFocus : function(){
9473 if(this.selectOnFocus){
9474 this.inputEl().dom.select();
9477 filterKeys : function(e){
9479 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9482 var c = e.getCharCode(), cc = String.fromCharCode(c);
9483 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9486 if(!this.maskRe.test(cc)){
9491 * Clear any invalid styles/messages for this field
9493 clearInvalid : function(){
9495 if(!this.el || this.preventMark){ // not rendered
9500 this.el.removeClass(this.invalidClass);
9502 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9504 var feedback = this.el.select('.form-control-feedback', true).first();
9507 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9513 this.indicator.removeClass('visible');
9514 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9517 this.fireEvent('valid', this);
9521 * Mark this field as valid
9523 markValid : function()
9525 if(!this.el || this.preventMark){ // not rendered...
9529 this.el.removeClass([this.invalidClass, this.validClass]);
9531 var feedback = this.el.select('.form-control-feedback', true).first();
9534 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9538 this.indicator.removeClass('visible');
9539 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9546 if(this.allowBlank && !this.getRawValue().length){
9550 this.el.addClass(this.validClass);
9552 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9554 var feedback = this.el.select('.form-control-feedback', true).first();
9557 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9558 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9563 this.fireEvent('valid', this);
9567 * Mark this field as invalid
9568 * @param {String} msg The validation message
9570 markInvalid : function(msg)
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]);
9588 if(this.allowBlank && !this.getRawValue().length){
9593 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9594 this.indicator.addClass('visible');
9597 this.el.addClass(this.invalidClass);
9599 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
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]);
9606 if(this.getValue().length || this.forceFeedback){
9607 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9614 this.fireEvent('invalid', this, msg);
9617 SafariOnKeyDown : function(event)
9619 // this is a workaround for a password hang bug on chrome/ webkit.
9620 if (this.inputEl().dom.type != 'password') {
9624 var isSelectAll = false;
9626 if(this.inputEl().dom.selectionEnd > 0){
9627 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9629 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9630 event.preventDefault();
9635 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9637 event.preventDefault();
9638 // this is very hacky as keydown always get's upper case.
9640 var cc = String.fromCharCode(event.getCharCode());
9641 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9645 adjustWidth : function(tag, w){
9646 tag = tag.toLowerCase();
9647 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9648 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9652 if(tag == 'textarea'){
9655 }else if(Roo.isOpera){
9659 if(tag == 'textarea'){
9667 setFieldLabel : function(v)
9674 var ar = this.el.select('label > span',true);
9676 if (ar.elements.length) {
9677 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9678 this.fieldLabel = v;
9682 var br = this.el.select('label',true);
9684 if(br.elements.length) {
9685 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9686 this.fieldLabel = v;
9690 Roo.log('Cannot Found any of label > span || label in input');
9694 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9695 this.fieldLabel = v;
9710 * @class Roo.bootstrap.TextArea
9711 * @extends Roo.bootstrap.Input
9712 * Bootstrap TextArea class
9713 * @cfg {Number} cols Specifies the visible width of a text area
9714 * @cfg {Number} rows Specifies the visible number of lines in a text area
9715 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9716 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9717 * @cfg {string} html text
9720 * Create a new TextArea
9721 * @param {Object} config The config object
9724 Roo.bootstrap.TextArea = function(config){
9725 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9729 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9739 getAutoCreate : function(){
9741 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9747 if(this.inputType != 'hidden'){
9748 cfg.cls = 'form-group' //input-group
9756 value : this.value || '',
9757 html: this.html || '',
9758 cls : 'form-control',
9759 placeholder : this.placeholder || ''
9763 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9764 input.maxLength = this.maxLength;
9768 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9772 input.cols = this.cols;
9775 if (this.readOnly) {
9776 input.readonly = true;
9780 input.name = this.name;
9784 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9788 ['xs','sm','md','lg'].map(function(size){
9789 if (settings[size]) {
9790 cfg.cls += ' col-' + size + '-' + settings[size];
9794 var inputblock = input;
9796 if(this.hasFeedback && !this.allowBlank){
9800 cls: 'glyphicon form-control-feedback'
9804 cls : 'has-feedback',
9813 if (this.before || this.after) {
9816 cls : 'input-group',
9820 inputblock.cn.push({
9822 cls : 'input-group-addon',
9827 inputblock.cn.push(input);
9829 if(this.hasFeedback && !this.allowBlank){
9830 inputblock.cls += ' has-feedback';
9831 inputblock.cn.push(feedback);
9835 inputblock.cn.push({
9837 cls : 'input-group-addon',
9844 if (align ==='left' && this.fieldLabel.length) {
9849 cls : 'control-label',
9850 html : this.fieldLabel
9861 if(this.labelWidth > 12){
9862 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9865 if(this.labelWidth < 13 && this.labelmd == 0){
9866 this.labelmd = this.labelWidth;
9869 if(this.labellg > 0){
9870 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9871 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9874 if(this.labelmd > 0){
9875 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9876 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9879 if(this.labelsm > 0){
9880 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9881 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9884 if(this.labelxs > 0){
9885 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9886 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9889 } else if ( this.fieldLabel.length) {
9894 //cls : 'input-group-addon',
9895 html : this.fieldLabel
9913 if (this.disabled) {
9914 input.disabled=true;
9921 * return the real textarea element.
9923 inputEl: function ()
9925 return this.el.select('textarea.form-control',true).first();
9929 * Clear any invalid styles/messages for this field
9931 clearInvalid : function()
9934 if(!this.el || this.preventMark){ // not rendered
9938 var label = this.el.select('label', true).first();
9939 var icon = this.el.select('i.fa-star', true).first();
9945 this.el.removeClass(this.invalidClass);
9947 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9949 var feedback = this.el.select('.form-control-feedback', true).first();
9952 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9957 this.fireEvent('valid', this);
9961 * Mark this field as valid
9963 markValid : function()
9965 if(!this.el || this.preventMark){ // not rendered
9969 this.el.removeClass([this.invalidClass, this.validClass]);
9971 var feedback = this.el.select('.form-control-feedback', true).first();
9974 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9977 if(this.disabled || this.allowBlank){
9981 var label = this.el.select('label', true).first();
9982 var icon = this.el.select('i.fa-star', true).first();
9988 this.el.addClass(this.validClass);
9990 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9992 var feedback = this.el.select('.form-control-feedback', true).first();
9995 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9996 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10001 this.fireEvent('valid', this);
10005 * Mark this field as invalid
10006 * @param {String} msg The validation message
10008 markInvalid : function(msg)
10010 if(!this.el || this.preventMark){ // not rendered
10014 this.el.removeClass([this.invalidClass, this.validClass]);
10016 var feedback = this.el.select('.form-control-feedback', true).first();
10019 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10022 if(this.disabled || this.allowBlank){
10026 var label = this.el.select('label', true).first();
10027 var icon = this.el.select('i.fa-star', true).first();
10029 if(!this.getValue().length && label && !icon){
10030 this.el.createChild({
10032 cls : 'text-danger fa fa-lg fa-star',
10033 tooltip : 'This field is required',
10034 style : 'margin-right:5px;'
10038 this.el.addClass(this.invalidClass);
10040 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10042 var feedback = this.el.select('.form-control-feedback', true).first();
10045 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10047 if(this.getValue().length || this.forceFeedback){
10048 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10055 this.fireEvent('invalid', this, msg);
10063 * trigger field - base class for combo..
10068 * @class Roo.bootstrap.TriggerField
10069 * @extends Roo.bootstrap.Input
10070 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10071 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10072 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10073 * for which you can provide a custom implementation. For example:
10075 var trigger = new Roo.bootstrap.TriggerField();
10076 trigger.onTriggerClick = myTriggerFn;
10077 trigger.applyTo('my-field');
10080 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10081 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10082 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10083 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10084 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10087 * Create a new TriggerField.
10088 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10089 * to the base TextField)
10091 Roo.bootstrap.TriggerField = function(config){
10092 this.mimicing = false;
10093 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10096 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10098 * @cfg {String} triggerClass A CSS class to apply to the trigger
10101 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10106 * @cfg {Boolean} removable (true|false) special filter default false
10110 /** @cfg {Boolean} grow @hide */
10111 /** @cfg {Number} growMin @hide */
10112 /** @cfg {Number} growMax @hide */
10118 autoSize: Roo.emptyFn,
10122 deferHeight : true,
10125 actionMode : 'wrap',
10130 getAutoCreate : function(){
10132 var align = this.labelAlign || this.parentLabelAlign();
10137 cls: 'form-group' //input-group
10144 type : this.inputType,
10145 cls : 'form-control',
10146 autocomplete: 'new-password',
10147 placeholder : this.placeholder || ''
10151 input.name = this.name;
10154 input.cls += ' input-' + this.size;
10157 if (this.disabled) {
10158 input.disabled=true;
10161 var inputblock = input;
10163 if(this.hasFeedback && !this.allowBlank){
10167 cls: 'glyphicon form-control-feedback'
10170 if(this.removable && !this.editable && !this.tickable){
10172 cls : 'has-feedback',
10178 cls : 'roo-combo-removable-btn close'
10185 cls : 'has-feedback',
10194 if(this.removable && !this.editable && !this.tickable){
10196 cls : 'roo-removable',
10202 cls : 'roo-combo-removable-btn close'
10209 if (this.before || this.after) {
10212 cls : 'input-group',
10216 inputblock.cn.push({
10218 cls : 'input-group-addon',
10223 inputblock.cn.push(input);
10225 if(this.hasFeedback && !this.allowBlank){
10226 inputblock.cls += ' has-feedback';
10227 inputblock.cn.push(feedback);
10231 inputblock.cn.push({
10233 cls : 'input-group-addon',
10246 cls: 'form-hidden-field'
10260 cls: 'form-hidden-field'
10264 cls: 'roo-select2-choices',
10268 cls: 'roo-select2-search-field',
10281 cls: 'roo-select2-container input-group',
10286 // cls: 'typeahead typeahead-long dropdown-menu',
10287 // style: 'display:none'
10292 if(!this.multiple && this.showToggleBtn){
10298 if (this.caret != false) {
10301 cls: 'fa fa-' + this.caret
10308 cls : 'input-group-addon btn dropdown-toggle',
10313 cls: 'combobox-clear',
10327 combobox.cls += ' roo-select2-container-multi';
10330 if (align ==='left' && this.fieldLabel.length) {
10332 cfg.cls += ' roo-form-group-label-left';
10337 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10338 tooltip : 'This field is required'
10343 cls : 'control-label',
10344 html : this.fieldLabel
10356 var labelCfg = cfg.cn[1];
10357 var contentCfg = cfg.cn[2];
10359 if(this.indicatorpos == 'right'){
10364 cls : 'control-label',
10368 html : this.fieldLabel
10372 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10373 tooltip : 'This field is required'
10386 labelCfg = cfg.cn[0];
10387 contentCfg = cfg.cn[1];
10390 if(this.labelWidth > 12){
10391 labelCfg.style = "width: " + this.labelWidth + 'px';
10394 if(this.labelWidth < 13 && this.labelmd == 0){
10395 this.labelmd = this.labelWidth;
10398 if(this.labellg > 0){
10399 labelCfg.cls += ' col-lg-' + this.labellg;
10400 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10403 if(this.labelmd > 0){
10404 labelCfg.cls += ' col-md-' + this.labelmd;
10405 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10408 if(this.labelsm > 0){
10409 labelCfg.cls += ' col-sm-' + this.labelsm;
10410 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10413 if(this.labelxs > 0){
10414 labelCfg.cls += ' col-xs-' + this.labelxs;
10415 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10418 } else if ( this.fieldLabel.length) {
10419 // Roo.log(" label");
10423 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10424 tooltip : 'This field is required'
10428 //cls : 'input-group-addon',
10429 html : this.fieldLabel
10437 if(this.indicatorpos == 'right'){
10445 html : this.fieldLabel
10449 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10450 tooltip : 'This field is required'
10463 // Roo.log(" no label && no align");
10470 ['xs','sm','md','lg'].map(function(size){
10471 if (settings[size]) {
10472 cfg.cls += ' col-' + size + '-' + settings[size];
10483 onResize : function(w, h){
10484 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10485 // if(typeof w == 'number'){
10486 // var x = w - this.trigger.getWidth();
10487 // this.inputEl().setWidth(this.adjustWidth('input', x));
10488 // this.trigger.setStyle('left', x+'px');
10493 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10496 getResizeEl : function(){
10497 return this.inputEl();
10501 getPositionEl : function(){
10502 return this.inputEl();
10506 alignErrorIcon : function(){
10507 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10511 initEvents : function(){
10515 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10516 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10517 if(!this.multiple && this.showToggleBtn){
10518 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10519 if(this.hideTrigger){
10520 this.trigger.setDisplayed(false);
10522 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10526 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10529 if(this.removable && !this.editable && !this.tickable){
10530 var close = this.closeTriggerEl();
10533 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10534 close.on('click', this.removeBtnClick, this, close);
10538 //this.trigger.addClassOnOver('x-form-trigger-over');
10539 //this.trigger.addClassOnClick('x-form-trigger-click');
10542 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10546 closeTriggerEl : function()
10548 var close = this.el.select('.roo-combo-removable-btn', true).first();
10549 return close ? close : false;
10552 removeBtnClick : function(e, h, el)
10554 e.preventDefault();
10556 if(this.fireEvent("remove", this) !== false){
10558 this.fireEvent("afterremove", this)
10562 createList : function()
10564 this.list = Roo.get(document.body).createChild({
10566 cls: 'typeahead typeahead-long dropdown-menu',
10567 style: 'display:none'
10570 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10575 initTrigger : function(){
10580 onDestroy : function(){
10582 this.trigger.removeAllListeners();
10583 // this.trigger.remove();
10586 // this.wrap.remove();
10588 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10592 onFocus : function(){
10593 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10595 if(!this.mimicing){
10596 this.wrap.addClass('x-trigger-wrap-focus');
10597 this.mimicing = true;
10598 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10599 if(this.monitorTab){
10600 this.el.on("keydown", this.checkTab, this);
10607 checkTab : function(e){
10608 if(e.getKey() == e.TAB){
10609 this.triggerBlur();
10614 onBlur : function(){
10619 mimicBlur : function(e, t){
10621 if(!this.wrap.contains(t) && this.validateBlur()){
10622 this.triggerBlur();
10628 triggerBlur : function(){
10629 this.mimicing = false;
10630 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10631 if(this.monitorTab){
10632 this.el.un("keydown", this.checkTab, this);
10634 //this.wrap.removeClass('x-trigger-wrap-focus');
10635 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10639 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10640 validateBlur : function(e, t){
10645 onDisable : function(){
10646 this.inputEl().dom.disabled = true;
10647 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10649 // this.wrap.addClass('x-item-disabled');
10654 onEnable : function(){
10655 this.inputEl().dom.disabled = false;
10656 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10658 // this.el.removeClass('x-item-disabled');
10663 onShow : function(){
10664 var ae = this.getActionEl();
10667 ae.dom.style.display = '';
10668 ae.dom.style.visibility = 'visible';
10674 onHide : function(){
10675 var ae = this.getActionEl();
10676 ae.dom.style.display = 'none';
10680 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10681 * by an implementing function.
10683 * @param {EventObject} e
10685 onTriggerClick : Roo.emptyFn
10689 * Ext JS Library 1.1.1
10690 * Copyright(c) 2006-2007, Ext JS, LLC.
10692 * Originally Released Under LGPL - original licence link has changed is not relivant.
10695 * <script type="text/javascript">
10700 * @class Roo.data.SortTypes
10702 * Defines the default sorting (casting?) comparison functions used when sorting data.
10704 Roo.data.SortTypes = {
10706 * Default sort that does nothing
10707 * @param {Mixed} s The value being converted
10708 * @return {Mixed} The comparison value
10710 none : function(s){
10715 * The regular expression used to strip tags
10719 stripTagsRE : /<\/?[^>]+>/gi,
10722 * Strips all HTML tags to sort on text only
10723 * @param {Mixed} s The value being converted
10724 * @return {String} The comparison value
10726 asText : function(s){
10727 return String(s).replace(this.stripTagsRE, "");
10731 * Strips all HTML tags to sort on text only - Case insensitive
10732 * @param {Mixed} s The value being converted
10733 * @return {String} The comparison value
10735 asUCText : function(s){
10736 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10740 * Case insensitive string
10741 * @param {Mixed} s The value being converted
10742 * @return {String} The comparison value
10744 asUCString : function(s) {
10745 return String(s).toUpperCase();
10750 * @param {Mixed} s The value being converted
10751 * @return {Number} The comparison value
10753 asDate : function(s) {
10757 if(s instanceof Date){
10758 return s.getTime();
10760 return Date.parse(String(s));
10765 * @param {Mixed} s The value being converted
10766 * @return {Float} The comparison value
10768 asFloat : function(s) {
10769 var val = parseFloat(String(s).replace(/,/g, ""));
10778 * @param {Mixed} s The value being converted
10779 * @return {Number} The comparison value
10781 asInt : function(s) {
10782 var val = parseInt(String(s).replace(/,/g, ""));
10790 * Ext JS Library 1.1.1
10791 * Copyright(c) 2006-2007, Ext JS, LLC.
10793 * Originally Released Under LGPL - original licence link has changed is not relivant.
10796 * <script type="text/javascript">
10800 * @class Roo.data.Record
10801 * Instances of this class encapsulate both record <em>definition</em> information, and record
10802 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10803 * to access Records cached in an {@link Roo.data.Store} object.<br>
10805 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10806 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10809 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10811 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10812 * {@link #create}. The parameters are the same.
10813 * @param {Array} data An associative Array of data values keyed by the field name.
10814 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10815 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10816 * not specified an integer id is generated.
10818 Roo.data.Record = function(data, id){
10819 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10824 * Generate a constructor for a specific record layout.
10825 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10826 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10827 * Each field definition object may contain the following properties: <ul>
10828 * <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,
10829 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10830 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10831 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10832 * is being used, then this is a string containing the javascript expression to reference the data relative to
10833 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10834 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10835 * this may be omitted.</p></li>
10836 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10837 * <ul><li>auto (Default, implies no conversion)</li>
10842 * <li>date</li></ul></p></li>
10843 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10844 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10845 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10846 * by the Reader into an object that will be stored in the Record. It is passed the
10847 * following parameters:<ul>
10848 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10850 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10852 * <br>usage:<br><pre><code>
10853 var TopicRecord = Roo.data.Record.create(
10854 {name: 'title', mapping: 'topic_title'},
10855 {name: 'author', mapping: 'username'},
10856 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10857 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10858 {name: 'lastPoster', mapping: 'user2'},
10859 {name: 'excerpt', mapping: 'post_text'}
10862 var myNewRecord = new TopicRecord({
10863 title: 'Do my job please',
10866 lastPost: new Date(),
10867 lastPoster: 'Animal',
10868 excerpt: 'No way dude!'
10870 myStore.add(myNewRecord);
10875 Roo.data.Record.create = function(o){
10876 var f = function(){
10877 f.superclass.constructor.apply(this, arguments);
10879 Roo.extend(f, Roo.data.Record);
10880 var p = f.prototype;
10881 p.fields = new Roo.util.MixedCollection(false, function(field){
10884 for(var i = 0, len = o.length; i < len; i++){
10885 p.fields.add(new Roo.data.Field(o[i]));
10887 f.getField = function(name){
10888 return p.fields.get(name);
10893 Roo.data.Record.AUTO_ID = 1000;
10894 Roo.data.Record.EDIT = 'edit';
10895 Roo.data.Record.REJECT = 'reject';
10896 Roo.data.Record.COMMIT = 'commit';
10898 Roo.data.Record.prototype = {
10900 * Readonly flag - true if this record has been modified.
10909 join : function(store){
10910 this.store = store;
10914 * Set the named field to the specified value.
10915 * @param {String} name The name of the field to set.
10916 * @param {Object} value The value to set the field to.
10918 set : function(name, value){
10919 if(this.data[name] == value){
10923 if(!this.modified){
10924 this.modified = {};
10926 if(typeof this.modified[name] == 'undefined'){
10927 this.modified[name] = this.data[name];
10929 this.data[name] = value;
10930 if(!this.editing && this.store){
10931 this.store.afterEdit(this);
10936 * Get the value of the named field.
10937 * @param {String} name The name of the field to get the value of.
10938 * @return {Object} The value of the field.
10940 get : function(name){
10941 return this.data[name];
10945 beginEdit : function(){
10946 this.editing = true;
10947 this.modified = {};
10951 cancelEdit : function(){
10952 this.editing = false;
10953 delete this.modified;
10957 endEdit : function(){
10958 this.editing = false;
10959 if(this.dirty && this.store){
10960 this.store.afterEdit(this);
10965 * Usually called by the {@link Roo.data.Store} which owns the Record.
10966 * Rejects all changes made to the Record since either creation, or the last commit operation.
10967 * Modified fields are reverted to their original values.
10969 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10970 * of reject operations.
10972 reject : function(){
10973 var m = this.modified;
10975 if(typeof m[n] != "function"){
10976 this.data[n] = m[n];
10979 this.dirty = false;
10980 delete this.modified;
10981 this.editing = false;
10983 this.store.afterReject(this);
10988 * Usually called by the {@link Roo.data.Store} which owns the Record.
10989 * Commits all changes made to the Record since either creation, or the last commit operation.
10991 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10992 * of commit operations.
10994 commit : function(){
10995 this.dirty = false;
10996 delete this.modified;
10997 this.editing = false;
10999 this.store.afterCommit(this);
11004 hasError : function(){
11005 return this.error != null;
11009 clearError : function(){
11014 * Creates a copy of this record.
11015 * @param {String} id (optional) A new record id if you don't want to use this record's id
11018 copy : function(newId) {
11019 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11023 * Ext JS Library 1.1.1
11024 * Copyright(c) 2006-2007, Ext JS, LLC.
11026 * Originally Released Under LGPL - original licence link has changed is not relivant.
11029 * <script type="text/javascript">
11035 * @class Roo.data.Store
11036 * @extends Roo.util.Observable
11037 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11038 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11040 * 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
11041 * has no knowledge of the format of the data returned by the Proxy.<br>
11043 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11044 * instances from the data object. These records are cached and made available through accessor functions.
11046 * Creates a new Store.
11047 * @param {Object} config A config object containing the objects needed for the Store to access data,
11048 * and read the data into Records.
11050 Roo.data.Store = function(config){
11051 this.data = new Roo.util.MixedCollection(false);
11052 this.data.getKey = function(o){
11055 this.baseParams = {};
11057 this.paramNames = {
11062 "multisort" : "_multisort"
11065 if(config && config.data){
11066 this.inlineData = config.data;
11067 delete config.data;
11070 Roo.apply(this, config);
11072 if(this.reader){ // reader passed
11073 this.reader = Roo.factory(this.reader, Roo.data);
11074 this.reader.xmodule = this.xmodule || false;
11075 if(!this.recordType){
11076 this.recordType = this.reader.recordType;
11078 if(this.reader.onMetaChange){
11079 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11083 if(this.recordType){
11084 this.fields = this.recordType.prototype.fields;
11086 this.modified = [];
11090 * @event datachanged
11091 * Fires when the data cache has changed, and a widget which is using this Store
11092 * as a Record cache should refresh its view.
11093 * @param {Store} this
11095 datachanged : true,
11097 * @event metachange
11098 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11099 * @param {Store} this
11100 * @param {Object} meta The JSON metadata
11105 * Fires when Records have been added to the Store
11106 * @param {Store} this
11107 * @param {Roo.data.Record[]} records The array of Records added
11108 * @param {Number} index The index at which the record(s) were added
11113 * Fires when a Record has been removed from the Store
11114 * @param {Store} this
11115 * @param {Roo.data.Record} record The Record that was removed
11116 * @param {Number} index The index at which the record was removed
11121 * Fires when a Record has been updated
11122 * @param {Store} this
11123 * @param {Roo.data.Record} record The Record that was updated
11124 * @param {String} operation The update operation being performed. Value may be one of:
11126 Roo.data.Record.EDIT
11127 Roo.data.Record.REJECT
11128 Roo.data.Record.COMMIT
11134 * Fires when the data cache has been cleared.
11135 * @param {Store} this
11139 * @event beforeload
11140 * Fires before a request is made for a new data object. If the beforeload handler returns false
11141 * the load action will be canceled.
11142 * @param {Store} this
11143 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11147 * @event beforeloadadd
11148 * Fires after a new set of Records has been loaded.
11149 * @param {Store} this
11150 * @param {Roo.data.Record[]} records The Records that were loaded
11151 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11153 beforeloadadd : true,
11156 * Fires after a new set of Records has been loaded, before they are added to the store.
11157 * @param {Store} this
11158 * @param {Roo.data.Record[]} records The Records that were loaded
11159 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11160 * @params {Object} return from reader
11164 * @event loadexception
11165 * Fires if an exception occurs in the Proxy during loading.
11166 * Called with the signature of the Proxy's "loadexception" event.
11167 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11170 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11171 * @param {Object} load options
11172 * @param {Object} jsonData from your request (normally this contains the Exception)
11174 loadexception : true
11178 this.proxy = Roo.factory(this.proxy, Roo.data);
11179 this.proxy.xmodule = this.xmodule || false;
11180 this.relayEvents(this.proxy, ["loadexception"]);
11182 this.sortToggle = {};
11183 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11185 Roo.data.Store.superclass.constructor.call(this);
11187 if(this.inlineData){
11188 this.loadData(this.inlineData);
11189 delete this.inlineData;
11193 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11195 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11196 * without a remote query - used by combo/forms at present.
11200 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11203 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11206 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11207 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11210 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11211 * on any HTTP request
11214 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11217 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11221 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11222 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11224 remoteSort : false,
11227 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11228 * loaded or when a record is removed. (defaults to false).
11230 pruneModifiedRecords : false,
11233 lastOptions : null,
11236 * Add Records to the Store and fires the add event.
11237 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11239 add : function(records){
11240 records = [].concat(records);
11241 for(var i = 0, len = records.length; i < len; i++){
11242 records[i].join(this);
11244 var index = this.data.length;
11245 this.data.addAll(records);
11246 this.fireEvent("add", this, records, index);
11250 * Remove a Record from the Store and fires the remove event.
11251 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11253 remove : function(record){
11254 var index = this.data.indexOf(record);
11255 this.data.removeAt(index);
11257 if(this.pruneModifiedRecords){
11258 this.modified.remove(record);
11260 this.fireEvent("remove", this, record, index);
11264 * Remove all Records from the Store and fires the clear event.
11266 removeAll : function(){
11268 if(this.pruneModifiedRecords){
11269 this.modified = [];
11271 this.fireEvent("clear", this);
11275 * Inserts Records to the Store at the given index and fires the add event.
11276 * @param {Number} index The start index at which to insert the passed Records.
11277 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11279 insert : function(index, records){
11280 records = [].concat(records);
11281 for(var i = 0, len = records.length; i < len; i++){
11282 this.data.insert(index, records[i]);
11283 records[i].join(this);
11285 this.fireEvent("add", this, records, index);
11289 * Get the index within the cache of the passed Record.
11290 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11291 * @return {Number} The index of the passed Record. Returns -1 if not found.
11293 indexOf : function(record){
11294 return this.data.indexOf(record);
11298 * Get the index within the cache of the Record with the passed id.
11299 * @param {String} id The id of the Record to find.
11300 * @return {Number} The index of the Record. Returns -1 if not found.
11302 indexOfId : function(id){
11303 return this.data.indexOfKey(id);
11307 * Get the Record with the specified id.
11308 * @param {String} id The id of the Record to find.
11309 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11311 getById : function(id){
11312 return this.data.key(id);
11316 * Get the Record at the specified index.
11317 * @param {Number} index The index of the Record to find.
11318 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11320 getAt : function(index){
11321 return this.data.itemAt(index);
11325 * Returns a range of Records between specified indices.
11326 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11327 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11328 * @return {Roo.data.Record[]} An array of Records
11330 getRange : function(start, end){
11331 return this.data.getRange(start, end);
11335 storeOptions : function(o){
11336 o = Roo.apply({}, o);
11339 this.lastOptions = o;
11343 * Loads the Record cache from the configured Proxy using the configured Reader.
11345 * If using remote paging, then the first load call must specify the <em>start</em>
11346 * and <em>limit</em> properties in the options.params property to establish the initial
11347 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11349 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11350 * and this call will return before the new data has been loaded. Perform any post-processing
11351 * in a callback function, or in a "load" event handler.</strong>
11353 * @param {Object} options An object containing properties which control loading options:<ul>
11354 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11355 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11356 * passed the following arguments:<ul>
11357 * <li>r : Roo.data.Record[]</li>
11358 * <li>options: Options object from the load call</li>
11359 * <li>success: Boolean success indicator</li></ul></li>
11360 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11361 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11364 load : function(options){
11365 options = options || {};
11366 if(this.fireEvent("beforeload", this, options) !== false){
11367 this.storeOptions(options);
11368 var p = Roo.apply(options.params || {}, this.baseParams);
11369 // if meta was not loaded from remote source.. try requesting it.
11370 if (!this.reader.metaFromRemote) {
11371 p._requestMeta = 1;
11373 if(this.sortInfo && this.remoteSort){
11374 var pn = this.paramNames;
11375 p[pn["sort"]] = this.sortInfo.field;
11376 p[pn["dir"]] = this.sortInfo.direction;
11378 if (this.multiSort) {
11379 var pn = this.paramNames;
11380 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11383 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11388 * Reloads the Record cache from the configured Proxy using the configured Reader and
11389 * the options from the last load operation performed.
11390 * @param {Object} options (optional) An object containing properties which may override the options
11391 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11392 * the most recently used options are reused).
11394 reload : function(options){
11395 this.load(Roo.applyIf(options||{}, this.lastOptions));
11399 // Called as a callback by the Reader during a load operation.
11400 loadRecords : function(o, options, success){
11401 if(!o || success === false){
11402 if(success !== false){
11403 this.fireEvent("load", this, [], options, o);
11405 if(options.callback){
11406 options.callback.call(options.scope || this, [], options, false);
11410 // if data returned failure - throw an exception.
11411 if (o.success === false) {
11412 // show a message if no listener is registered.
11413 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11414 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11416 // loadmask wil be hooked into this..
11417 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11420 var r = o.records, t = o.totalRecords || r.length;
11422 this.fireEvent("beforeloadadd", this, r, options, o);
11424 if(!options || options.add !== true){
11425 if(this.pruneModifiedRecords){
11426 this.modified = [];
11428 for(var i = 0, len = r.length; i < len; i++){
11432 this.data = this.snapshot;
11433 delete this.snapshot;
11436 this.data.addAll(r);
11437 this.totalLength = t;
11439 this.fireEvent("datachanged", this);
11441 this.totalLength = Math.max(t, this.data.length+r.length);
11445 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11447 var e = new Roo.data.Record({});
11449 e.set(this.parent.displayField, this.parent.emptyTitle);
11450 e.set(this.parent.valueField, '');
11455 this.fireEvent("load", this, r, options, o);
11456 if(options.callback){
11457 options.callback.call(options.scope || this, r, options, true);
11463 * Loads data from a passed data block. A Reader which understands the format of the data
11464 * must have been configured in the constructor.
11465 * @param {Object} data The data block from which to read the Records. The format of the data expected
11466 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11467 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11469 loadData : function(o, append){
11470 var r = this.reader.readRecords(o);
11471 this.loadRecords(r, {add: append}, true);
11475 * Gets the number of cached records.
11477 * <em>If using paging, this may not be the total size of the dataset. If the data object
11478 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11479 * the data set size</em>
11481 getCount : function(){
11482 return this.data.length || 0;
11486 * Gets the total number of records in the dataset as returned by the server.
11488 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11489 * the dataset size</em>
11491 getTotalCount : function(){
11492 return this.totalLength || 0;
11496 * Returns the sort state of the Store as an object with two properties:
11498 field {String} The name of the field by which the Records are sorted
11499 direction {String} The sort order, "ASC" or "DESC"
11502 getSortState : function(){
11503 return this.sortInfo;
11507 applySort : function(){
11508 if(this.sortInfo && !this.remoteSort){
11509 var s = this.sortInfo, f = s.field;
11510 var st = this.fields.get(f).sortType;
11511 var fn = function(r1, r2){
11512 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11513 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11515 this.data.sort(s.direction, fn);
11516 if(this.snapshot && this.snapshot != this.data){
11517 this.snapshot.sort(s.direction, fn);
11523 * Sets the default sort column and order to be used by the next load operation.
11524 * @param {String} fieldName The name of the field to sort by.
11525 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11527 setDefaultSort : function(field, dir){
11528 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11532 * Sort the Records.
11533 * If remote sorting is used, the sort is performed on the server, and the cache is
11534 * reloaded. If local sorting is used, the cache is sorted internally.
11535 * @param {String} fieldName The name of the field to sort by.
11536 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11538 sort : function(fieldName, dir){
11539 var f = this.fields.get(fieldName);
11541 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11543 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11544 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11549 this.sortToggle[f.name] = dir;
11550 this.sortInfo = {field: f.name, direction: dir};
11551 if(!this.remoteSort){
11553 this.fireEvent("datachanged", this);
11555 this.load(this.lastOptions);
11560 * Calls the specified function for each of the Records in the cache.
11561 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11562 * Returning <em>false</em> aborts and exits the iteration.
11563 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11565 each : function(fn, scope){
11566 this.data.each(fn, scope);
11570 * Gets all records modified since the last commit. Modified records are persisted across load operations
11571 * (e.g., during paging).
11572 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11574 getModifiedRecords : function(){
11575 return this.modified;
11579 createFilterFn : function(property, value, anyMatch){
11580 if(!value.exec){ // not a regex
11581 value = String(value);
11582 if(value.length == 0){
11585 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11587 return function(r){
11588 return value.test(r.data[property]);
11593 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11594 * @param {String} property A field on your records
11595 * @param {Number} start The record index to start at (defaults to 0)
11596 * @param {Number} end The last record index to include (defaults to length - 1)
11597 * @return {Number} The sum
11599 sum : function(property, start, end){
11600 var rs = this.data.items, v = 0;
11601 start = start || 0;
11602 end = (end || end === 0) ? end : rs.length-1;
11604 for(var i = start; i <= end; i++){
11605 v += (rs[i].data[property] || 0);
11611 * Filter the records by a specified property.
11612 * @param {String} field A field on your records
11613 * @param {String/RegExp} value Either a string that the field
11614 * should start with or a RegExp to test against the field
11615 * @param {Boolean} anyMatch True to match any part not just the beginning
11617 filter : function(property, value, anyMatch){
11618 var fn = this.createFilterFn(property, value, anyMatch);
11619 return fn ? this.filterBy(fn) : this.clearFilter();
11623 * Filter by a function. The specified function will be called with each
11624 * record in this data source. If the function returns true the record is included,
11625 * otherwise it is filtered.
11626 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11627 * @param {Object} scope (optional) The scope of the function (defaults to this)
11629 filterBy : function(fn, scope){
11630 this.snapshot = this.snapshot || this.data;
11631 this.data = this.queryBy(fn, scope||this);
11632 this.fireEvent("datachanged", this);
11636 * Query the records by a specified property.
11637 * @param {String} field A field on your records
11638 * @param {String/RegExp} value Either a string that the field
11639 * should start with or a RegExp to test against the field
11640 * @param {Boolean} anyMatch True to match any part not just the beginning
11641 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11643 query : function(property, value, anyMatch){
11644 var fn = this.createFilterFn(property, value, anyMatch);
11645 return fn ? this.queryBy(fn) : this.data.clone();
11649 * Query by a function. The specified function will be called with each
11650 * record in this data source. If the function returns true the record is included
11652 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11653 * @param {Object} scope (optional) The scope of the function (defaults to this)
11654 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11656 queryBy : function(fn, scope){
11657 var data = this.snapshot || this.data;
11658 return data.filterBy(fn, scope||this);
11662 * Collects unique values for a particular dataIndex from this store.
11663 * @param {String} dataIndex The property to collect
11664 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11665 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11666 * @return {Array} An array of the unique values
11668 collect : function(dataIndex, allowNull, bypassFilter){
11669 var d = (bypassFilter === true && this.snapshot) ?
11670 this.snapshot.items : this.data.items;
11671 var v, sv, r = [], l = {};
11672 for(var i = 0, len = d.length; i < len; i++){
11673 v = d[i].data[dataIndex];
11675 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11684 * Revert to a view of the Record cache with no filtering applied.
11685 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11687 clearFilter : function(suppressEvent){
11688 if(this.snapshot && this.snapshot != this.data){
11689 this.data = this.snapshot;
11690 delete this.snapshot;
11691 if(suppressEvent !== true){
11692 this.fireEvent("datachanged", this);
11698 afterEdit : function(record){
11699 if(this.modified.indexOf(record) == -1){
11700 this.modified.push(record);
11702 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11706 afterReject : function(record){
11707 this.modified.remove(record);
11708 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11712 afterCommit : function(record){
11713 this.modified.remove(record);
11714 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11718 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11719 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11721 commitChanges : function(){
11722 var m = this.modified.slice(0);
11723 this.modified = [];
11724 for(var i = 0, len = m.length; i < len; i++){
11730 * Cancel outstanding changes on all changed records.
11732 rejectChanges : function(){
11733 var m = this.modified.slice(0);
11734 this.modified = [];
11735 for(var i = 0, len = m.length; i < len; i++){
11740 onMetaChange : function(meta, rtype, o){
11741 this.recordType = rtype;
11742 this.fields = rtype.prototype.fields;
11743 delete this.snapshot;
11744 this.sortInfo = meta.sortInfo || this.sortInfo;
11745 this.modified = [];
11746 this.fireEvent('metachange', this, this.reader.meta);
11749 moveIndex : function(data, type)
11751 var index = this.indexOf(data);
11753 var newIndex = index + type;
11757 this.insert(newIndex, data);
11762 * Ext JS Library 1.1.1
11763 * Copyright(c) 2006-2007, Ext JS, LLC.
11765 * Originally Released Under LGPL - original licence link has changed is not relivant.
11768 * <script type="text/javascript">
11772 * @class Roo.data.SimpleStore
11773 * @extends Roo.data.Store
11774 * Small helper class to make creating Stores from Array data easier.
11775 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11776 * @cfg {Array} fields An array of field definition objects, or field name strings.
11777 * @cfg {Array} data The multi-dimensional array of data
11779 * @param {Object} config
11781 Roo.data.SimpleStore = function(config){
11782 Roo.data.SimpleStore.superclass.constructor.call(this, {
11784 reader: new Roo.data.ArrayReader({
11787 Roo.data.Record.create(config.fields)
11789 proxy : new Roo.data.MemoryProxy(config.data)
11793 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11795 * Ext JS Library 1.1.1
11796 * Copyright(c) 2006-2007, Ext JS, LLC.
11798 * Originally Released Under LGPL - original licence link has changed is not relivant.
11801 * <script type="text/javascript">
11806 * @extends Roo.data.Store
11807 * @class Roo.data.JsonStore
11808 * Small helper class to make creating Stores for JSON data easier. <br/>
11810 var store = new Roo.data.JsonStore({
11811 url: 'get-images.php',
11813 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11816 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11817 * JsonReader and HttpProxy (unless inline data is provided).</b>
11818 * @cfg {Array} fields An array of field definition objects, or field name strings.
11820 * @param {Object} config
11822 Roo.data.JsonStore = function(c){
11823 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11824 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11825 reader: new Roo.data.JsonReader(c, c.fields)
11828 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11830 * Ext JS Library 1.1.1
11831 * Copyright(c) 2006-2007, Ext JS, LLC.
11833 * Originally Released Under LGPL - original licence link has changed is not relivant.
11836 * <script type="text/javascript">
11840 Roo.data.Field = function(config){
11841 if(typeof config == "string"){
11842 config = {name: config};
11844 Roo.apply(this, config);
11847 this.type = "auto";
11850 var st = Roo.data.SortTypes;
11851 // named sortTypes are supported, here we look them up
11852 if(typeof this.sortType == "string"){
11853 this.sortType = st[this.sortType];
11856 // set default sortType for strings and dates
11857 if(!this.sortType){
11860 this.sortType = st.asUCString;
11863 this.sortType = st.asDate;
11866 this.sortType = st.none;
11871 var stripRe = /[\$,%]/g;
11873 // prebuilt conversion function for this field, instead of
11874 // switching every time we're reading a value
11876 var cv, dateFormat = this.dateFormat;
11881 cv = function(v){ return v; };
11884 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11888 return v !== undefined && v !== null && v !== '' ?
11889 parseInt(String(v).replace(stripRe, ""), 10) : '';
11894 return v !== undefined && v !== null && v !== '' ?
11895 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11900 cv = function(v){ return v === true || v === "true" || v == 1; };
11907 if(v instanceof Date){
11911 if(dateFormat == "timestamp"){
11912 return new Date(v*1000);
11914 return Date.parseDate(v, dateFormat);
11916 var parsed = Date.parse(v);
11917 return parsed ? new Date(parsed) : null;
11926 Roo.data.Field.prototype = {
11934 * Ext JS Library 1.1.1
11935 * Copyright(c) 2006-2007, Ext JS, LLC.
11937 * Originally Released Under LGPL - original licence link has changed is not relivant.
11940 * <script type="text/javascript">
11943 // Base class for reading structured data from a data source. This class is intended to be
11944 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11947 * @class Roo.data.DataReader
11948 * Base class for reading structured data from a data source. This class is intended to be
11949 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11952 Roo.data.DataReader = function(meta, recordType){
11956 this.recordType = recordType instanceof Array ?
11957 Roo.data.Record.create(recordType) : recordType;
11960 Roo.data.DataReader.prototype = {
11962 * Create an empty record
11963 * @param {Object} data (optional) - overlay some values
11964 * @return {Roo.data.Record} record created.
11966 newRow : function(d) {
11968 this.recordType.prototype.fields.each(function(c) {
11970 case 'int' : da[c.name] = 0; break;
11971 case 'date' : da[c.name] = new Date(); break;
11972 case 'float' : da[c.name] = 0.0; break;
11973 case 'boolean' : da[c.name] = false; break;
11974 default : da[c.name] = ""; break;
11978 return new this.recordType(Roo.apply(da, d));
11983 * Ext JS Library 1.1.1
11984 * Copyright(c) 2006-2007, Ext JS, LLC.
11986 * Originally Released Under LGPL - original licence link has changed is not relivant.
11989 * <script type="text/javascript">
11993 * @class Roo.data.DataProxy
11994 * @extends Roo.data.Observable
11995 * This class is an abstract base class for implementations which provide retrieval of
11996 * unformatted data objects.<br>
11998 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11999 * (of the appropriate type which knows how to parse the data object) to provide a block of
12000 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12002 * Custom implementations must implement the load method as described in
12003 * {@link Roo.data.HttpProxy#load}.
12005 Roo.data.DataProxy = function(){
12008 * @event beforeload
12009 * Fires before a network request is made to retrieve a data object.
12010 * @param {Object} This DataProxy object.
12011 * @param {Object} params The params parameter to the load function.
12016 * Fires before the load method's callback is called.
12017 * @param {Object} This DataProxy object.
12018 * @param {Object} o The data object.
12019 * @param {Object} arg The callback argument object passed to the load function.
12023 * @event loadexception
12024 * Fires if an Exception occurs during data retrieval.
12025 * @param {Object} This DataProxy object.
12026 * @param {Object} o The data object.
12027 * @param {Object} arg The callback argument object passed to the load function.
12028 * @param {Object} e The Exception.
12030 loadexception : true
12032 Roo.data.DataProxy.superclass.constructor.call(this);
12035 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12038 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12042 * Ext JS Library 1.1.1
12043 * Copyright(c) 2006-2007, Ext JS, LLC.
12045 * Originally Released Under LGPL - original licence link has changed is not relivant.
12048 * <script type="text/javascript">
12051 * @class Roo.data.MemoryProxy
12052 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12053 * to the Reader when its load method is called.
12055 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12057 Roo.data.MemoryProxy = function(data){
12061 Roo.data.MemoryProxy.superclass.constructor.call(this);
12065 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12068 * Load data from the requested source (in this case an in-memory
12069 * data object passed to the constructor), read the data object into
12070 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12071 * process that block using the passed callback.
12072 * @param {Object} params This parameter is not used by the MemoryProxy class.
12073 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12074 * object into a block of Roo.data.Records.
12075 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12076 * The function must be passed <ul>
12077 * <li>The Record block object</li>
12078 * <li>The "arg" argument from the load function</li>
12079 * <li>A boolean success indicator</li>
12081 * @param {Object} scope The scope in which to call the callback
12082 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12084 load : function(params, reader, callback, scope, arg){
12085 params = params || {};
12088 result = reader.readRecords(this.data);
12090 this.fireEvent("loadexception", this, arg, null, e);
12091 callback.call(scope, null, arg, false);
12094 callback.call(scope, result, arg, true);
12098 update : function(params, records){
12103 * Ext JS Library 1.1.1
12104 * Copyright(c) 2006-2007, Ext JS, LLC.
12106 * Originally Released Under LGPL - original licence link has changed is not relivant.
12109 * <script type="text/javascript">
12112 * @class Roo.data.HttpProxy
12113 * @extends Roo.data.DataProxy
12114 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12115 * configured to reference a certain URL.<br><br>
12117 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12118 * from which the running page was served.<br><br>
12120 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12122 * Be aware that to enable the browser to parse an XML document, the server must set
12123 * the Content-Type header in the HTTP response to "text/xml".
12125 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12126 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12127 * will be used to make the request.
12129 Roo.data.HttpProxy = function(conn){
12130 Roo.data.HttpProxy.superclass.constructor.call(this);
12131 // is conn a conn config or a real conn?
12133 this.useAjax = !conn || !conn.events;
12137 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12138 // thse are take from connection...
12141 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12144 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12145 * extra parameters to each request made by this object. (defaults to undefined)
12148 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12149 * to each request made by this object. (defaults to undefined)
12152 * @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)
12155 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12158 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12164 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12168 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12169 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12170 * a finer-grained basis than the DataProxy events.
12172 getConnection : function(){
12173 return this.useAjax ? Roo.Ajax : this.conn;
12177 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12178 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12179 * process that block using the passed callback.
12180 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12181 * for the request to the remote server.
12182 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12183 * object into a block of Roo.data.Records.
12184 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12185 * The function must be passed <ul>
12186 * <li>The Record block object</li>
12187 * <li>The "arg" argument from the load function</li>
12188 * <li>A boolean success indicator</li>
12190 * @param {Object} scope The scope in which to call the callback
12191 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12193 load : function(params, reader, callback, scope, arg){
12194 if(this.fireEvent("beforeload", this, params) !== false){
12196 params : params || {},
12198 callback : callback,
12203 callback : this.loadResponse,
12207 Roo.applyIf(o, this.conn);
12208 if(this.activeRequest){
12209 Roo.Ajax.abort(this.activeRequest);
12211 this.activeRequest = Roo.Ajax.request(o);
12213 this.conn.request(o);
12216 callback.call(scope||this, null, arg, false);
12221 loadResponse : function(o, success, response){
12222 delete this.activeRequest;
12224 this.fireEvent("loadexception", this, o, response);
12225 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12230 result = o.reader.read(response);
12232 this.fireEvent("loadexception", this, o, response, e);
12233 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12237 this.fireEvent("load", this, o, o.request.arg);
12238 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12242 update : function(dataSet){
12247 updateResponse : function(dataSet){
12252 * Ext JS Library 1.1.1
12253 * Copyright(c) 2006-2007, Ext JS, LLC.
12255 * Originally Released Under LGPL - original licence link has changed is not relivant.
12258 * <script type="text/javascript">
12262 * @class Roo.data.ScriptTagProxy
12263 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12264 * other than the originating domain of the running page.<br><br>
12266 * <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
12267 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12269 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12270 * source code that is used as the source inside a <script> tag.<br><br>
12272 * In order for the browser to process the returned data, the server must wrap the data object
12273 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12274 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12275 * depending on whether the callback name was passed:
12278 boolean scriptTag = false;
12279 String cb = request.getParameter("callback");
12282 response.setContentType("text/javascript");
12284 response.setContentType("application/x-json");
12286 Writer out = response.getWriter();
12288 out.write(cb + "(");
12290 out.print(dataBlock.toJsonString());
12297 * @param {Object} config A configuration object.
12299 Roo.data.ScriptTagProxy = function(config){
12300 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12301 Roo.apply(this, config);
12302 this.head = document.getElementsByTagName("head")[0];
12305 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12307 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12309 * @cfg {String} url The URL from which to request the data object.
12312 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12316 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12317 * the server the name of the callback function set up by the load call to process the returned data object.
12318 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12319 * javascript output which calls this named function passing the data object as its only parameter.
12321 callbackParam : "callback",
12323 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12324 * name to the request.
12329 * Load data from the configured URL, read the data object into
12330 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12331 * process that block using the passed callback.
12332 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12333 * for the request to the remote server.
12334 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12335 * object into a block of Roo.data.Records.
12336 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12337 * The function must be passed <ul>
12338 * <li>The Record block object</li>
12339 * <li>The "arg" argument from the load function</li>
12340 * <li>A boolean success indicator</li>
12342 * @param {Object} scope The scope in which to call the callback
12343 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12345 load : function(params, reader, callback, scope, arg){
12346 if(this.fireEvent("beforeload", this, params) !== false){
12348 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12350 var url = this.url;
12351 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12353 url += "&_dc=" + (new Date().getTime());
12355 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12358 cb : "stcCallback"+transId,
12359 scriptId : "stcScript"+transId,
12363 callback : callback,
12369 window[trans.cb] = function(o){
12370 conn.handleResponse(o, trans);
12373 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12375 if(this.autoAbort !== false){
12379 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12381 var script = document.createElement("script");
12382 script.setAttribute("src", url);
12383 script.setAttribute("type", "text/javascript");
12384 script.setAttribute("id", trans.scriptId);
12385 this.head.appendChild(script);
12387 this.trans = trans;
12389 callback.call(scope||this, null, arg, false);
12394 isLoading : function(){
12395 return this.trans ? true : false;
12399 * Abort the current server request.
12401 abort : function(){
12402 if(this.isLoading()){
12403 this.destroyTrans(this.trans);
12408 destroyTrans : function(trans, isLoaded){
12409 this.head.removeChild(document.getElementById(trans.scriptId));
12410 clearTimeout(trans.timeoutId);
12412 window[trans.cb] = undefined;
12414 delete window[trans.cb];
12417 // if hasn't been loaded, wait for load to remove it to prevent script error
12418 window[trans.cb] = function(){
12419 window[trans.cb] = undefined;
12421 delete window[trans.cb];
12428 handleResponse : function(o, trans){
12429 this.trans = false;
12430 this.destroyTrans(trans, true);
12433 result = trans.reader.readRecords(o);
12435 this.fireEvent("loadexception", this, o, trans.arg, e);
12436 trans.callback.call(trans.scope||window, null, trans.arg, false);
12439 this.fireEvent("load", this, o, trans.arg);
12440 trans.callback.call(trans.scope||window, result, trans.arg, true);
12444 handleFailure : function(trans){
12445 this.trans = false;
12446 this.destroyTrans(trans, false);
12447 this.fireEvent("loadexception", this, null, trans.arg);
12448 trans.callback.call(trans.scope||window, null, trans.arg, false);
12452 * Ext JS Library 1.1.1
12453 * Copyright(c) 2006-2007, Ext JS, LLC.
12455 * Originally Released Under LGPL - original licence link has changed is not relivant.
12458 * <script type="text/javascript">
12462 * @class Roo.data.JsonReader
12463 * @extends Roo.data.DataReader
12464 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12465 * based on mappings in a provided Roo.data.Record constructor.
12467 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12468 * in the reply previously.
12473 var RecordDef = Roo.data.Record.create([
12474 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12475 {name: 'occupation'} // This field will use "occupation" as the mapping.
12477 var myReader = new Roo.data.JsonReader({
12478 totalProperty: "results", // The property which contains the total dataset size (optional)
12479 root: "rows", // The property which contains an Array of row objects
12480 id: "id" // The property within each row object that provides an ID for the record (optional)
12484 * This would consume a JSON file like this:
12486 { 'results': 2, 'rows': [
12487 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12488 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12491 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12492 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12493 * paged from the remote server.
12494 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12495 * @cfg {String} root name of the property which contains the Array of row objects.
12496 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12497 * @cfg {Array} fields Array of field definition objects
12499 * Create a new JsonReader
12500 * @param {Object} meta Metadata configuration options
12501 * @param {Object} recordType Either an Array of field definition objects,
12502 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12504 Roo.data.JsonReader = function(meta, recordType){
12507 // set some defaults:
12508 Roo.applyIf(meta, {
12509 totalProperty: 'total',
12510 successProperty : 'success',
12515 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12517 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12520 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12521 * Used by Store query builder to append _requestMeta to params.
12524 metaFromRemote : false,
12526 * This method is only used by a DataProxy which has retrieved data from a remote server.
12527 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12528 * @return {Object} data A data block which is used by an Roo.data.Store object as
12529 * a cache of Roo.data.Records.
12531 read : function(response){
12532 var json = response.responseText;
12534 var o = /* eval:var:o */ eval("("+json+")");
12536 throw {message: "JsonReader.read: Json object not found"};
12542 this.metaFromRemote = true;
12543 this.meta = o.metaData;
12544 this.recordType = Roo.data.Record.create(o.metaData.fields);
12545 this.onMetaChange(this.meta, this.recordType, o);
12547 return this.readRecords(o);
12550 // private function a store will implement
12551 onMetaChange : function(meta, recordType, o){
12558 simpleAccess: function(obj, subsc) {
12565 getJsonAccessor: function(){
12567 return function(expr) {
12569 return(re.test(expr))
12570 ? new Function("obj", "return obj." + expr)
12575 return Roo.emptyFn;
12580 * Create a data block containing Roo.data.Records from an XML document.
12581 * @param {Object} o An object which contains an Array of row objects in the property specified
12582 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12583 * which contains the total size of the dataset.
12584 * @return {Object} data A data block which is used by an Roo.data.Store object as
12585 * a cache of Roo.data.Records.
12587 readRecords : function(o){
12589 * After any data loads, the raw JSON data is available for further custom processing.
12593 var s = this.meta, Record = this.recordType,
12594 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12596 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12598 if(s.totalProperty) {
12599 this.getTotal = this.getJsonAccessor(s.totalProperty);
12601 if(s.successProperty) {
12602 this.getSuccess = this.getJsonAccessor(s.successProperty);
12604 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12606 var g = this.getJsonAccessor(s.id);
12607 this.getId = function(rec) {
12609 return (r === undefined || r === "") ? null : r;
12612 this.getId = function(){return null;};
12615 for(var jj = 0; jj < fl; jj++){
12617 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12618 this.ef[jj] = this.getJsonAccessor(map);
12622 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12623 if(s.totalProperty){
12624 var vt = parseInt(this.getTotal(o), 10);
12629 if(s.successProperty){
12630 var vs = this.getSuccess(o);
12631 if(vs === false || vs === 'false'){
12636 for(var i = 0; i < c; i++){
12639 var id = this.getId(n);
12640 for(var j = 0; j < fl; j++){
12642 var v = this.ef[j](n);
12644 Roo.log('missing convert for ' + f.name);
12648 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12650 var record = new Record(values, id);
12652 records[i] = record;
12658 totalRecords : totalRecords
12663 * Ext JS Library 1.1.1
12664 * Copyright(c) 2006-2007, Ext JS, LLC.
12666 * Originally Released Under LGPL - original licence link has changed is not relivant.
12669 * <script type="text/javascript">
12673 * @class Roo.data.ArrayReader
12674 * @extends Roo.data.DataReader
12675 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12676 * Each element of that Array represents a row of data fields. The
12677 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12678 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12682 var RecordDef = Roo.data.Record.create([
12683 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12684 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12686 var myReader = new Roo.data.ArrayReader({
12687 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12691 * This would consume an Array like this:
12693 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12695 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12697 * Create a new JsonReader
12698 * @param {Object} meta Metadata configuration options.
12699 * @param {Object} recordType Either an Array of field definition objects
12700 * as specified to {@link Roo.data.Record#create},
12701 * or an {@link Roo.data.Record} object
12702 * created using {@link Roo.data.Record#create}.
12704 Roo.data.ArrayReader = function(meta, recordType){
12705 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12708 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12710 * Create a data block containing Roo.data.Records from an XML document.
12711 * @param {Object} o An Array of row objects which represents the dataset.
12712 * @return {Object} data A data block which is used by an Roo.data.Store object as
12713 * a cache of Roo.data.Records.
12715 readRecords : function(o){
12716 var sid = this.meta ? this.meta.id : null;
12717 var recordType = this.recordType, fields = recordType.prototype.fields;
12720 for(var i = 0; i < root.length; i++){
12723 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12724 for(var j = 0, jlen = fields.length; j < jlen; j++){
12725 var f = fields.items[j];
12726 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12727 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12729 values[f.name] = v;
12731 var record = new recordType(values, id);
12733 records[records.length] = record;
12737 totalRecords : records.length
12746 * @class Roo.bootstrap.ComboBox
12747 * @extends Roo.bootstrap.TriggerField
12748 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12749 * @cfg {Boolean} append (true|false) default false
12750 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12751 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12752 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12753 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12754 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12755 * @cfg {Boolean} animate default true
12756 * @cfg {Boolean} emptyResultText only for touch device
12757 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12758 * @cfg {String} emptyTitle default ''
12760 * Create a new ComboBox.
12761 * @param {Object} config Configuration options
12763 Roo.bootstrap.ComboBox = function(config){
12764 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12768 * Fires when the dropdown list is expanded
12769 * @param {Roo.bootstrap.ComboBox} combo This combo box
12774 * Fires when the dropdown list is collapsed
12775 * @param {Roo.bootstrap.ComboBox} combo This combo box
12779 * @event beforeselect
12780 * Fires before a list item is selected. Return false to cancel the selection.
12781 * @param {Roo.bootstrap.ComboBox} combo This combo box
12782 * @param {Roo.data.Record} record The data record returned from the underlying store
12783 * @param {Number} index The index of the selected item in the dropdown list
12785 'beforeselect' : true,
12788 * Fires when a list item is selected
12789 * @param {Roo.bootstrap.ComboBox} combo This combo box
12790 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12791 * @param {Number} index The index of the selected item in the dropdown list
12795 * @event beforequery
12796 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12797 * The event object passed has these properties:
12798 * @param {Roo.bootstrap.ComboBox} combo This combo box
12799 * @param {String} query The query
12800 * @param {Boolean} forceAll true to force "all" query
12801 * @param {Boolean} cancel true to cancel the query
12802 * @param {Object} e The query event object
12804 'beforequery': true,
12807 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12808 * @param {Roo.bootstrap.ComboBox} combo This combo box
12813 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12814 * @param {Roo.bootstrap.ComboBox} combo This combo box
12815 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12820 * Fires when the remove value from the combobox array
12821 * @param {Roo.bootstrap.ComboBox} combo This combo box
12825 * @event afterremove
12826 * Fires when the remove value from the combobox array
12827 * @param {Roo.bootstrap.ComboBox} combo This combo box
12829 'afterremove' : true,
12831 * @event specialfilter
12832 * Fires when specialfilter
12833 * @param {Roo.bootstrap.ComboBox} combo This combo box
12835 'specialfilter' : true,
12838 * Fires when tick the element
12839 * @param {Roo.bootstrap.ComboBox} combo This combo box
12843 * @event touchviewdisplay
12844 * Fires when touch view require special display (default is using displayField)
12845 * @param {Roo.bootstrap.ComboBox} combo This combo box
12846 * @param {Object} cfg set html .
12848 'touchviewdisplay' : true
12853 this.tickItems = [];
12855 this.selectedIndex = -1;
12856 if(this.mode == 'local'){
12857 if(config.queryDelay === undefined){
12858 this.queryDelay = 10;
12860 if(config.minChars === undefined){
12866 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12869 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12870 * rendering into an Roo.Editor, defaults to false)
12873 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12874 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12877 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12880 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12881 * the dropdown list (defaults to undefined, with no header element)
12885 * @cfg {String/Roo.Template} tpl The template to use to render the output
12889 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12891 listWidth: undefined,
12893 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12894 * mode = 'remote' or 'text' if mode = 'local')
12896 displayField: undefined,
12899 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12900 * mode = 'remote' or 'value' if mode = 'local').
12901 * Note: use of a valueField requires the user make a selection
12902 * in order for a value to be mapped.
12904 valueField: undefined,
12906 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12911 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12912 * field's data value (defaults to the underlying DOM element's name)
12914 hiddenName: undefined,
12916 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12920 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12922 selectedClass: 'active',
12925 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12929 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12930 * anchor positions (defaults to 'tl-bl')
12932 listAlign: 'tl-bl?',
12934 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12938 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12939 * query specified by the allQuery config option (defaults to 'query')
12941 triggerAction: 'query',
12943 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12944 * (defaults to 4, does not apply if editable = false)
12948 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12949 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12953 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12954 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12958 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12959 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12963 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12964 * when editable = true (defaults to false)
12966 selectOnFocus:false,
12968 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12970 queryParam: 'query',
12972 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12973 * when mode = 'remote' (defaults to 'Loading...')
12975 loadingText: 'Loading...',
12977 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12981 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12985 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12986 * traditional select (defaults to true)
12990 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12994 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12998 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12999 * listWidth has a higher value)
13003 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13004 * allow the user to set arbitrary text into the field (defaults to false)
13006 forceSelection:false,
13008 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13009 * if typeAhead = true (defaults to 250)
13011 typeAheadDelay : 250,
13013 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13014 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13016 valueNotFoundText : undefined,
13018 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13020 blockFocus : false,
13023 * @cfg {Boolean} disableClear Disable showing of clear button.
13025 disableClear : false,
13027 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13029 alwaysQuery : false,
13032 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13037 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13039 invalidClass : "has-warning",
13042 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13044 validClass : "has-success",
13047 * @cfg {Boolean} specialFilter (true|false) special filter default false
13049 specialFilter : false,
13052 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13054 mobileTouchView : true,
13057 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13059 useNativeIOS : false,
13062 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13064 mobile_restrict_height : false,
13066 ios_options : false,
13078 btnPosition : 'right',
13079 triggerList : true,
13080 showToggleBtn : true,
13082 emptyResultText: 'Empty',
13083 triggerText : 'Select',
13086 // element that contains real text value.. (when hidden is used..)
13088 getAutoCreate : function()
13093 * Render classic select for iso
13096 if(Roo.isIOS && this.useNativeIOS){
13097 cfg = this.getAutoCreateNativeIOS();
13105 if(Roo.isTouch && this.mobileTouchView){
13106 cfg = this.getAutoCreateTouchView();
13113 if(!this.tickable){
13114 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13119 * ComboBox with tickable selections
13122 var align = this.labelAlign || this.parentLabelAlign();
13125 cls : 'form-group roo-combobox-tickable' //input-group
13128 var btn_text_select = '';
13129 var btn_text_done = '';
13130 var btn_text_cancel = '';
13132 if (this.btn_text_show) {
13133 btn_text_select = 'Select';
13134 btn_text_done = 'Done';
13135 btn_text_cancel = 'Cancel';
13140 cls : 'tickable-buttons',
13145 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13146 //html : this.triggerText
13147 html: btn_text_select
13153 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13155 html: btn_text_done
13161 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13163 html: btn_text_cancel
13169 buttons.cn.unshift({
13171 cls: 'roo-select2-search-field-input'
13177 Roo.each(buttons.cn, function(c){
13179 c.cls += ' btn-' + _this.size;
13182 if (_this.disabled) {
13193 cls: 'form-hidden-field'
13197 cls: 'roo-select2-choices',
13201 cls: 'roo-select2-search-field',
13212 cls: 'roo-select2-container input-group roo-select2-container-multi',
13217 // cls: 'typeahead typeahead-long dropdown-menu',
13218 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13223 if(this.hasFeedback && !this.allowBlank){
13227 cls: 'glyphicon form-control-feedback'
13230 combobox.cn.push(feedback);
13234 if (align ==='left' && this.fieldLabel.length) {
13236 cfg.cls += ' roo-form-group-label-left';
13241 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13242 tooltip : 'This field is required'
13247 cls : 'control-label',
13248 html : this.fieldLabel
13260 var labelCfg = cfg.cn[1];
13261 var contentCfg = cfg.cn[2];
13264 if(this.indicatorpos == 'right'){
13270 cls : 'control-label',
13274 html : this.fieldLabel
13278 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13279 tooltip : 'This field is required'
13294 labelCfg = cfg.cn[0];
13295 contentCfg = cfg.cn[1];
13299 if(this.labelWidth > 12){
13300 labelCfg.style = "width: " + this.labelWidth + 'px';
13303 if(this.labelWidth < 13 && this.labelmd == 0){
13304 this.labelmd = this.labelWidth;
13307 if(this.labellg > 0){
13308 labelCfg.cls += ' col-lg-' + this.labellg;
13309 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13312 if(this.labelmd > 0){
13313 labelCfg.cls += ' col-md-' + this.labelmd;
13314 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13317 if(this.labelsm > 0){
13318 labelCfg.cls += ' col-sm-' + this.labelsm;
13319 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13322 if(this.labelxs > 0){
13323 labelCfg.cls += ' col-xs-' + this.labelxs;
13324 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13328 } else if ( this.fieldLabel.length) {
13329 // Roo.log(" label");
13333 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13334 tooltip : 'This field is required'
13338 //cls : 'input-group-addon',
13339 html : this.fieldLabel
13344 if(this.indicatorpos == 'right'){
13348 //cls : 'input-group-addon',
13349 html : this.fieldLabel
13353 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13354 tooltip : 'This field is required'
13363 // Roo.log(" no label && no align");
13370 ['xs','sm','md','lg'].map(function(size){
13371 if (settings[size]) {
13372 cfg.cls += ' col-' + size + '-' + settings[size];
13380 _initEventsCalled : false,
13383 initEvents: function()
13385 if (this._initEventsCalled) { // as we call render... prevent looping...
13388 this._initEventsCalled = true;
13391 throw "can not find store for combo";
13394 this.indicator = this.indicatorEl();
13396 this.store = Roo.factory(this.store, Roo.data);
13397 this.store.parent = this;
13399 // if we are building from html. then this element is so complex, that we can not really
13400 // use the rendered HTML.
13401 // so we have to trash and replace the previous code.
13402 if (Roo.XComponent.build_from_html) {
13403 // remove this element....
13404 var e = this.el.dom, k=0;
13405 while (e ) { e = e.previousSibling; ++k;}
13410 this.rendered = false;
13412 this.render(this.parent().getChildContainer(true), k);
13415 if(Roo.isIOS && this.useNativeIOS){
13416 this.initIOSView();
13424 if(Roo.isTouch && this.mobileTouchView){
13425 this.initTouchView();
13430 this.initTickableEvents();
13434 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13436 if(this.hiddenName){
13438 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13440 this.hiddenField.dom.value =
13441 this.hiddenValue !== undefined ? this.hiddenValue :
13442 this.value !== undefined ? this.value : '';
13444 // prevent input submission
13445 this.el.dom.removeAttribute('name');
13446 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13451 // this.el.dom.setAttribute('autocomplete', 'off');
13454 var cls = 'x-combo-list';
13456 //this.list = new Roo.Layer({
13457 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13463 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13464 _this.list.setWidth(lw);
13467 this.list.on('mouseover', this.onViewOver, this);
13468 this.list.on('mousemove', this.onViewMove, this);
13469 this.list.on('scroll', this.onViewScroll, this);
13472 this.list.swallowEvent('mousewheel');
13473 this.assetHeight = 0;
13476 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13477 this.assetHeight += this.header.getHeight();
13480 this.innerList = this.list.createChild({cls:cls+'-inner'});
13481 this.innerList.on('mouseover', this.onViewOver, this);
13482 this.innerList.on('mousemove', this.onViewMove, this);
13483 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13485 if(this.allowBlank && !this.pageSize && !this.disableClear){
13486 this.footer = this.list.createChild({cls:cls+'-ft'});
13487 this.pageTb = new Roo.Toolbar(this.footer);
13491 this.footer = this.list.createChild({cls:cls+'-ft'});
13492 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13493 {pageSize: this.pageSize});
13497 if (this.pageTb && this.allowBlank && !this.disableClear) {
13499 this.pageTb.add(new Roo.Toolbar.Fill(), {
13500 cls: 'x-btn-icon x-btn-clear',
13502 handler: function()
13505 _this.clearValue();
13506 _this.onSelect(false, -1);
13511 this.assetHeight += this.footer.getHeight();
13516 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13519 this.view = new Roo.View(this.list, this.tpl, {
13520 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13522 //this.view.wrapEl.setDisplayed(false);
13523 this.view.on('click', this.onViewClick, this);
13526 this.store.on('beforeload', this.onBeforeLoad, this);
13527 this.store.on('load', this.onLoad, this);
13528 this.store.on('loadexception', this.onLoadException, this);
13530 if(this.resizable){
13531 this.resizer = new Roo.Resizable(this.list, {
13532 pinned:true, handles:'se'
13534 this.resizer.on('resize', function(r, w, h){
13535 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13536 this.listWidth = w;
13537 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13538 this.restrictHeight();
13540 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13543 if(!this.editable){
13544 this.editable = true;
13545 this.setEditable(false);
13550 if (typeof(this.events.add.listeners) != 'undefined') {
13552 this.addicon = this.wrap.createChild(
13553 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13555 this.addicon.on('click', function(e) {
13556 this.fireEvent('add', this);
13559 if (typeof(this.events.edit.listeners) != 'undefined') {
13561 this.editicon = this.wrap.createChild(
13562 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13563 if (this.addicon) {
13564 this.editicon.setStyle('margin-left', '40px');
13566 this.editicon.on('click', function(e) {
13568 // we fire even if inothing is selected..
13569 this.fireEvent('edit', this, this.lastData );
13575 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13576 "up" : function(e){
13577 this.inKeyMode = true;
13581 "down" : function(e){
13582 if(!this.isExpanded()){
13583 this.onTriggerClick();
13585 this.inKeyMode = true;
13590 "enter" : function(e){
13591 // this.onViewClick();
13595 if(this.fireEvent("specialkey", this, e)){
13596 this.onViewClick(false);
13602 "esc" : function(e){
13606 "tab" : function(e){
13609 if(this.fireEvent("specialkey", this, e)){
13610 this.onViewClick(false);
13618 doRelay : function(foo, bar, hname){
13619 if(hname == 'down' || this.scope.isExpanded()){
13620 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13629 this.queryDelay = Math.max(this.queryDelay || 10,
13630 this.mode == 'local' ? 10 : 250);
13633 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13635 if(this.typeAhead){
13636 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13638 if(this.editable !== false){
13639 this.inputEl().on("keyup", this.onKeyUp, this);
13641 if(this.forceSelection){
13642 this.inputEl().on('blur', this.doForce, this);
13646 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13647 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13651 initTickableEvents: function()
13655 if(this.hiddenName){
13657 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13659 this.hiddenField.dom.value =
13660 this.hiddenValue !== undefined ? this.hiddenValue :
13661 this.value !== undefined ? this.value : '';
13663 // prevent input submission
13664 this.el.dom.removeAttribute('name');
13665 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13670 // this.list = this.el.select('ul.dropdown-menu',true).first();
13672 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13673 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13674 if(this.triggerList){
13675 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13678 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13679 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13681 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13682 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13684 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13685 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13687 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13688 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13689 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13692 this.cancelBtn.hide();
13697 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13698 _this.list.setWidth(lw);
13701 this.list.on('mouseover', this.onViewOver, this);
13702 this.list.on('mousemove', this.onViewMove, this);
13704 this.list.on('scroll', this.onViewScroll, this);
13707 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13708 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13711 this.view = new Roo.View(this.list, this.tpl, {
13716 selectedClass: this.selectedClass
13719 //this.view.wrapEl.setDisplayed(false);
13720 this.view.on('click', this.onViewClick, this);
13724 this.store.on('beforeload', this.onBeforeLoad, this);
13725 this.store.on('load', this.onLoad, this);
13726 this.store.on('loadexception', this.onLoadException, this);
13729 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13730 "up" : function(e){
13731 this.inKeyMode = true;
13735 "down" : function(e){
13736 this.inKeyMode = true;
13740 "enter" : function(e){
13741 if(this.fireEvent("specialkey", this, e)){
13742 this.onViewClick(false);
13748 "esc" : function(e){
13749 this.onTickableFooterButtonClick(e, false, false);
13752 "tab" : function(e){
13753 this.fireEvent("specialkey", this, e);
13755 this.onTickableFooterButtonClick(e, false, false);
13762 doRelay : function(e, fn, key){
13763 if(this.scope.isExpanded()){
13764 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13773 this.queryDelay = Math.max(this.queryDelay || 10,
13774 this.mode == 'local' ? 10 : 250);
13777 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13779 if(this.typeAhead){
13780 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13783 if(this.editable !== false){
13784 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13787 this.indicator = this.indicatorEl();
13789 if(this.indicator){
13790 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13791 this.indicator.hide();
13796 onDestroy : function(){
13798 this.view.setStore(null);
13799 this.view.el.removeAllListeners();
13800 this.view.el.remove();
13801 this.view.purgeListeners();
13804 this.list.dom.innerHTML = '';
13808 this.store.un('beforeload', this.onBeforeLoad, this);
13809 this.store.un('load', this.onLoad, this);
13810 this.store.un('loadexception', this.onLoadException, this);
13812 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13816 fireKey : function(e){
13817 if(e.isNavKeyPress() && !this.list.isVisible()){
13818 this.fireEvent("specialkey", this, e);
13823 onResize: function(w, h){
13824 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13826 // if(typeof w != 'number'){
13827 // // we do not handle it!?!?
13830 // var tw = this.trigger.getWidth();
13831 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13832 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13834 // this.inputEl().setWidth( this.adjustWidth('input', x));
13836 // //this.trigger.setStyle('left', x+'px');
13838 // if(this.list && this.listWidth === undefined){
13839 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13840 // this.list.setWidth(lw);
13841 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13849 * Allow or prevent the user from directly editing the field text. If false is passed,
13850 * the user will only be able to select from the items defined in the dropdown list. This method
13851 * is the runtime equivalent of setting the 'editable' config option at config time.
13852 * @param {Boolean} value True to allow the user to directly edit the field text
13854 setEditable : function(value){
13855 if(value == this.editable){
13858 this.editable = value;
13860 this.inputEl().dom.setAttribute('readOnly', true);
13861 this.inputEl().on('mousedown', this.onTriggerClick, this);
13862 this.inputEl().addClass('x-combo-noedit');
13864 this.inputEl().dom.setAttribute('readOnly', false);
13865 this.inputEl().un('mousedown', this.onTriggerClick, this);
13866 this.inputEl().removeClass('x-combo-noedit');
13872 onBeforeLoad : function(combo,opts){
13873 if(!this.hasFocus){
13877 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13879 this.restrictHeight();
13880 this.selectedIndex = -1;
13884 onLoad : function(){
13886 this.hasQuery = false;
13888 if(!this.hasFocus){
13892 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13893 this.loading.hide();
13896 if(this.store.getCount() > 0){
13899 this.restrictHeight();
13900 if(this.lastQuery == this.allQuery){
13901 if(this.editable && !this.tickable){
13902 this.inputEl().dom.select();
13906 !this.selectByValue(this.value, true) &&
13909 !this.store.lastOptions ||
13910 typeof(this.store.lastOptions.add) == 'undefined' ||
13911 this.store.lastOptions.add != true
13914 this.select(0, true);
13917 if(this.autoFocus){
13920 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13921 this.taTask.delay(this.typeAheadDelay);
13925 this.onEmptyResults();
13931 onLoadException : function()
13933 this.hasQuery = false;
13935 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13936 this.loading.hide();
13939 if(this.tickable && this.editable){
13944 // only causes errors at present
13945 //Roo.log(this.store.reader.jsonData);
13946 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13948 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13954 onTypeAhead : function(){
13955 if(this.store.getCount() > 0){
13956 var r = this.store.getAt(0);
13957 var newValue = r.data[this.displayField];
13958 var len = newValue.length;
13959 var selStart = this.getRawValue().length;
13961 if(selStart != len){
13962 this.setRawValue(newValue);
13963 this.selectText(selStart, newValue.length);
13969 onSelect : function(record, index){
13971 if(this.fireEvent('beforeselect', this, record, index) !== false){
13973 this.setFromData(index > -1 ? record.data : false);
13976 this.fireEvent('select', this, record, index);
13981 * Returns the currently selected field value or empty string if no value is set.
13982 * @return {String} value The selected value
13984 getValue : function()
13986 if(Roo.isIOS && this.useNativeIOS){
13987 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13991 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13994 if(this.valueField){
13995 return typeof this.value != 'undefined' ? this.value : '';
13997 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14001 getRawValue : function()
14003 if(Roo.isIOS && this.useNativeIOS){
14004 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14007 var v = this.inputEl().getValue();
14013 * Clears any text/value currently set in the field
14015 clearValue : function(){
14017 if(this.hiddenField){
14018 this.hiddenField.dom.value = '';
14021 this.setRawValue('');
14022 this.lastSelectionText = '';
14023 this.lastData = false;
14025 var close = this.closeTriggerEl();
14036 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14037 * will be displayed in the field. If the value does not match the data value of an existing item,
14038 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14039 * Otherwise the field will be blank (although the value will still be set).
14040 * @param {String} value The value to match
14042 setValue : function(v)
14044 if(Roo.isIOS && this.useNativeIOS){
14045 this.setIOSValue(v);
14055 if(this.valueField){
14056 var r = this.findRecord(this.valueField, v);
14058 text = r.data[this.displayField];
14059 }else if(this.valueNotFoundText !== undefined){
14060 text = this.valueNotFoundText;
14063 this.lastSelectionText = text;
14064 if(this.hiddenField){
14065 this.hiddenField.dom.value = v;
14067 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14070 var close = this.closeTriggerEl();
14073 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14079 * @property {Object} the last set data for the element
14084 * Sets the value of the field based on a object which is related to the record format for the store.
14085 * @param {Object} value the value to set as. or false on reset?
14087 setFromData : function(o){
14094 var dv = ''; // display value
14095 var vv = ''; // value value..
14097 if (this.displayField) {
14098 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14100 // this is an error condition!!!
14101 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14104 if(this.valueField){
14105 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14108 var close = this.closeTriggerEl();
14111 if(dv.length || vv * 1 > 0){
14113 this.blockFocus=true;
14119 if(this.hiddenField){
14120 this.hiddenField.dom.value = vv;
14122 this.lastSelectionText = dv;
14123 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14127 // no hidden field.. - we store the value in 'value', but still display
14128 // display field!!!!
14129 this.lastSelectionText = dv;
14130 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14137 reset : function(){
14138 // overridden so that last data is reset..
14145 this.setValue(this.originalValue);
14146 //this.clearInvalid();
14147 this.lastData = false;
14149 this.view.clearSelections();
14155 findRecord : function(prop, value){
14157 if(this.store.getCount() > 0){
14158 this.store.each(function(r){
14159 if(r.data[prop] == value){
14169 getName: function()
14171 // returns hidden if it's set..
14172 if (!this.rendered) {return ''};
14173 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14177 onViewMove : function(e, t){
14178 this.inKeyMode = false;
14182 onViewOver : function(e, t){
14183 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14186 var item = this.view.findItemFromChild(t);
14189 var index = this.view.indexOf(item);
14190 this.select(index, false);
14195 onViewClick : function(view, doFocus, el, e)
14197 var index = this.view.getSelectedIndexes()[0];
14199 var r = this.store.getAt(index);
14203 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14210 Roo.each(this.tickItems, function(v,k){
14212 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14214 _this.tickItems.splice(k, 1);
14216 if(typeof(e) == 'undefined' && view == false){
14217 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14229 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14230 this.tickItems.push(r.data);
14233 if(typeof(e) == 'undefined' && view == false){
14234 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14241 this.onSelect(r, index);
14243 if(doFocus !== false && !this.blockFocus){
14244 this.inputEl().focus();
14249 restrictHeight : function(){
14250 //this.innerList.dom.style.height = '';
14251 //var inner = this.innerList.dom;
14252 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14253 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14254 //this.list.beginUpdate();
14255 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14256 this.list.alignTo(this.inputEl(), this.listAlign);
14257 this.list.alignTo(this.inputEl(), this.listAlign);
14258 //this.list.endUpdate();
14262 onEmptyResults : function(){
14264 if(this.tickable && this.editable){
14265 this.hasFocus = false;
14266 this.restrictHeight();
14274 * Returns true if the dropdown list is expanded, else false.
14276 isExpanded : function(){
14277 return this.list.isVisible();
14281 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14282 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14283 * @param {String} value The data value of the item to select
14284 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14285 * selected item if it is not currently in view (defaults to true)
14286 * @return {Boolean} True if the value matched an item in the list, else false
14288 selectByValue : function(v, scrollIntoView){
14289 if(v !== undefined && v !== null){
14290 var r = this.findRecord(this.valueField || this.displayField, v);
14292 this.select(this.store.indexOf(r), scrollIntoView);
14300 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14301 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14302 * @param {Number} index The zero-based index of the list item to select
14303 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14304 * selected item if it is not currently in view (defaults to true)
14306 select : function(index, scrollIntoView){
14307 this.selectedIndex = index;
14308 this.view.select(index);
14309 if(scrollIntoView !== false){
14310 var el = this.view.getNode(index);
14312 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14315 this.list.scrollChildIntoView(el, false);
14321 selectNext : function(){
14322 var ct = this.store.getCount();
14324 if(this.selectedIndex == -1){
14326 }else if(this.selectedIndex < ct-1){
14327 this.select(this.selectedIndex+1);
14333 selectPrev : function(){
14334 var ct = this.store.getCount();
14336 if(this.selectedIndex == -1){
14338 }else if(this.selectedIndex != 0){
14339 this.select(this.selectedIndex-1);
14345 onKeyUp : function(e){
14346 if(this.editable !== false && !e.isSpecialKey()){
14347 this.lastKey = e.getKey();
14348 this.dqTask.delay(this.queryDelay);
14353 validateBlur : function(){
14354 return !this.list || !this.list.isVisible();
14358 initQuery : function(){
14360 var v = this.getRawValue();
14362 if(this.tickable && this.editable){
14363 v = this.tickableInputEl().getValue();
14370 doForce : function(){
14371 if(this.inputEl().dom.value.length > 0){
14372 this.inputEl().dom.value =
14373 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14379 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14380 * query allowing the query action to be canceled if needed.
14381 * @param {String} query The SQL query to execute
14382 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14383 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14384 * saved in the current store (defaults to false)
14386 doQuery : function(q, forceAll){
14388 if(q === undefined || q === null){
14393 forceAll: forceAll,
14397 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14402 forceAll = qe.forceAll;
14403 if(forceAll === true || (q.length >= this.minChars)){
14405 this.hasQuery = true;
14407 if(this.lastQuery != q || this.alwaysQuery){
14408 this.lastQuery = q;
14409 if(this.mode == 'local'){
14410 this.selectedIndex = -1;
14412 this.store.clearFilter();
14415 if(this.specialFilter){
14416 this.fireEvent('specialfilter', this);
14421 this.store.filter(this.displayField, q);
14424 this.store.fireEvent("datachanged", this.store);
14431 this.store.baseParams[this.queryParam] = q;
14433 var options = {params : this.getParams(q)};
14436 options.add = true;
14437 options.params.start = this.page * this.pageSize;
14440 this.store.load(options);
14443 * this code will make the page width larger, at the beginning, the list not align correctly,
14444 * we should expand the list on onLoad
14445 * so command out it
14450 this.selectedIndex = -1;
14455 this.loadNext = false;
14459 getParams : function(q){
14461 //p[this.queryParam] = q;
14465 p.limit = this.pageSize;
14471 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14473 collapse : function(){
14474 if(!this.isExpanded()){
14480 this.hasFocus = false;
14484 this.cancelBtn.hide();
14485 this.trigger.show();
14488 this.tickableInputEl().dom.value = '';
14489 this.tickableInputEl().blur();
14494 Roo.get(document).un('mousedown', this.collapseIf, this);
14495 Roo.get(document).un('mousewheel', this.collapseIf, this);
14496 if (!this.editable) {
14497 Roo.get(document).un('keydown', this.listKeyPress, this);
14499 this.fireEvent('collapse', this);
14505 collapseIf : function(e){
14506 var in_combo = e.within(this.el);
14507 var in_list = e.within(this.list);
14508 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14510 if (in_combo || in_list || is_list) {
14511 //e.stopPropagation();
14516 this.onTickableFooterButtonClick(e, false, false);
14524 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14526 expand : function(){
14528 if(this.isExpanded() || !this.hasFocus){
14532 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14533 this.list.setWidth(lw);
14539 this.restrictHeight();
14543 this.tickItems = Roo.apply([], this.item);
14546 this.cancelBtn.show();
14547 this.trigger.hide();
14550 this.tickableInputEl().focus();
14555 Roo.get(document).on('mousedown', this.collapseIf, this);
14556 Roo.get(document).on('mousewheel', this.collapseIf, this);
14557 if (!this.editable) {
14558 Roo.get(document).on('keydown', this.listKeyPress, this);
14561 this.fireEvent('expand', this);
14565 // Implements the default empty TriggerField.onTriggerClick function
14566 onTriggerClick : function(e)
14568 Roo.log('trigger click');
14570 if(this.disabled || !this.triggerList){
14575 this.loadNext = false;
14577 if(this.isExpanded()){
14579 if (!this.blockFocus) {
14580 this.inputEl().focus();
14584 this.hasFocus = true;
14585 if(this.triggerAction == 'all') {
14586 this.doQuery(this.allQuery, true);
14588 this.doQuery(this.getRawValue());
14590 if (!this.blockFocus) {
14591 this.inputEl().focus();
14596 onTickableTriggerClick : function(e)
14603 this.loadNext = false;
14604 this.hasFocus = true;
14606 if(this.triggerAction == 'all') {
14607 this.doQuery(this.allQuery, true);
14609 this.doQuery(this.getRawValue());
14613 onSearchFieldClick : function(e)
14615 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14616 this.onTickableFooterButtonClick(e, false, false);
14620 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14625 this.loadNext = false;
14626 this.hasFocus = true;
14628 if(this.triggerAction == 'all') {
14629 this.doQuery(this.allQuery, true);
14631 this.doQuery(this.getRawValue());
14635 listKeyPress : function(e)
14637 //Roo.log('listkeypress');
14638 // scroll to first matching element based on key pres..
14639 if (e.isSpecialKey()) {
14642 var k = String.fromCharCode(e.getKey()).toUpperCase();
14645 var csel = this.view.getSelectedNodes();
14646 var cselitem = false;
14648 var ix = this.view.indexOf(csel[0]);
14649 cselitem = this.store.getAt(ix);
14650 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14656 this.store.each(function(v) {
14658 // start at existing selection.
14659 if (cselitem.id == v.id) {
14665 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14666 match = this.store.indexOf(v);
14672 if (match === false) {
14673 return true; // no more action?
14676 this.view.select(match);
14677 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14678 sn.scrollIntoView(sn.dom.parentNode, false);
14681 onViewScroll : function(e, t){
14683 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){
14687 this.hasQuery = true;
14689 this.loading = this.list.select('.loading', true).first();
14691 if(this.loading === null){
14692 this.list.createChild({
14694 cls: 'loading roo-select2-more-results roo-select2-active',
14695 html: 'Loading more results...'
14698 this.loading = this.list.select('.loading', true).first();
14700 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14702 this.loading.hide();
14705 this.loading.show();
14710 this.loadNext = true;
14712 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14717 addItem : function(o)
14719 var dv = ''; // display value
14721 if (this.displayField) {
14722 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14724 // this is an error condition!!!
14725 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14732 var choice = this.choices.createChild({
14734 cls: 'roo-select2-search-choice',
14743 cls: 'roo-select2-search-choice-close fa fa-times',
14748 }, this.searchField);
14750 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14752 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14760 this.inputEl().dom.value = '';
14765 onRemoveItem : function(e, _self, o)
14767 e.preventDefault();
14769 this.lastItem = Roo.apply([], this.item);
14771 var index = this.item.indexOf(o.data) * 1;
14774 Roo.log('not this item?!');
14778 this.item.splice(index, 1);
14783 this.fireEvent('remove', this, e);
14789 syncValue : function()
14791 if(!this.item.length){
14798 Roo.each(this.item, function(i){
14799 if(_this.valueField){
14800 value.push(i[_this.valueField]);
14807 this.value = value.join(',');
14809 if(this.hiddenField){
14810 this.hiddenField.dom.value = this.value;
14813 this.store.fireEvent("datachanged", this.store);
14818 clearItem : function()
14820 if(!this.multiple){
14826 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14834 if(this.tickable && !Roo.isTouch){
14835 this.view.refresh();
14839 inputEl: function ()
14841 if(Roo.isIOS && this.useNativeIOS){
14842 return this.el.select('select.roo-ios-select', true).first();
14845 if(Roo.isTouch && this.mobileTouchView){
14846 return this.el.select('input.form-control',true).first();
14850 return this.searchField;
14853 return this.el.select('input.form-control',true).first();
14856 onTickableFooterButtonClick : function(e, btn, el)
14858 e.preventDefault();
14860 this.lastItem = Roo.apply([], this.item);
14862 if(btn && btn.name == 'cancel'){
14863 this.tickItems = Roo.apply([], this.item);
14872 Roo.each(this.tickItems, function(o){
14880 validate : function()
14882 if(this.getVisibilityEl().hasClass('hidden')){
14886 var v = this.getRawValue();
14889 v = this.getValue();
14892 if(this.disabled || this.allowBlank || v.length){
14897 this.markInvalid();
14901 tickableInputEl : function()
14903 if(!this.tickable || !this.editable){
14904 return this.inputEl();
14907 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14911 getAutoCreateTouchView : function()
14916 cls: 'form-group' //input-group
14922 type : this.inputType,
14923 cls : 'form-control x-combo-noedit',
14924 autocomplete: 'new-password',
14925 placeholder : this.placeholder || '',
14930 input.name = this.name;
14934 input.cls += ' input-' + this.size;
14937 if (this.disabled) {
14938 input.disabled = true;
14949 inputblock.cls += ' input-group';
14951 inputblock.cn.unshift({
14953 cls : 'input-group-addon',
14958 if(this.removable && !this.multiple){
14959 inputblock.cls += ' roo-removable';
14961 inputblock.cn.push({
14964 cls : 'roo-combo-removable-btn close'
14968 if(this.hasFeedback && !this.allowBlank){
14970 inputblock.cls += ' has-feedback';
14972 inputblock.cn.push({
14974 cls: 'glyphicon form-control-feedback'
14981 inputblock.cls += (this.before) ? '' : ' input-group';
14983 inputblock.cn.push({
14985 cls : 'input-group-addon',
14996 cls: 'form-hidden-field'
15010 cls: 'form-hidden-field'
15014 cls: 'roo-select2-choices',
15018 cls: 'roo-select2-search-field',
15031 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15037 if(!this.multiple && this.showToggleBtn){
15044 if (this.caret != false) {
15047 cls: 'fa fa-' + this.caret
15054 cls : 'input-group-addon btn dropdown-toggle',
15059 cls: 'combobox-clear',
15073 combobox.cls += ' roo-select2-container-multi';
15076 var align = this.labelAlign || this.parentLabelAlign();
15078 if (align ==='left' && this.fieldLabel.length) {
15083 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15084 tooltip : 'This field is required'
15088 cls : 'control-label',
15089 html : this.fieldLabel
15100 var labelCfg = cfg.cn[1];
15101 var contentCfg = cfg.cn[2];
15104 if(this.indicatorpos == 'right'){
15109 cls : 'control-label',
15113 html : this.fieldLabel
15117 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15118 tooltip : 'This field is required'
15131 labelCfg = cfg.cn[0];
15132 contentCfg = cfg.cn[1];
15137 if(this.labelWidth > 12){
15138 labelCfg.style = "width: " + this.labelWidth + 'px';
15141 if(this.labelWidth < 13 && this.labelmd == 0){
15142 this.labelmd = this.labelWidth;
15145 if(this.labellg > 0){
15146 labelCfg.cls += ' col-lg-' + this.labellg;
15147 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15150 if(this.labelmd > 0){
15151 labelCfg.cls += ' col-md-' + this.labelmd;
15152 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15155 if(this.labelsm > 0){
15156 labelCfg.cls += ' col-sm-' + this.labelsm;
15157 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15160 if(this.labelxs > 0){
15161 labelCfg.cls += ' col-xs-' + this.labelxs;
15162 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15166 } else if ( this.fieldLabel.length) {
15170 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15171 tooltip : 'This field is required'
15175 cls : 'control-label',
15176 html : this.fieldLabel
15187 if(this.indicatorpos == 'right'){
15191 cls : 'control-label',
15192 html : this.fieldLabel,
15196 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15197 tooltip : 'This field is required'
15214 var settings = this;
15216 ['xs','sm','md','lg'].map(function(size){
15217 if (settings[size]) {
15218 cfg.cls += ' col-' + size + '-' + settings[size];
15225 initTouchView : function()
15227 this.renderTouchView();
15229 this.touchViewEl.on('scroll', function(){
15230 this.el.dom.scrollTop = 0;
15233 this.originalValue = this.getValue();
15235 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15237 this.inputEl().on("click", this.showTouchView, this);
15238 if (this.triggerEl) {
15239 this.triggerEl.on("click", this.showTouchView, this);
15243 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15244 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15246 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15248 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15249 this.store.on('load', this.onTouchViewLoad, this);
15250 this.store.on('loadexception', this.onTouchViewLoadException, this);
15252 if(this.hiddenName){
15254 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15256 this.hiddenField.dom.value =
15257 this.hiddenValue !== undefined ? this.hiddenValue :
15258 this.value !== undefined ? this.value : '';
15260 this.el.dom.removeAttribute('name');
15261 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15265 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15266 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15269 if(this.removable && !this.multiple){
15270 var close = this.closeTriggerEl();
15272 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15273 close.on('click', this.removeBtnClick, this, close);
15277 * fix the bug in Safari iOS8
15279 this.inputEl().on("focus", function(e){
15280 document.activeElement.blur();
15283 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15290 renderTouchView : function()
15292 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15293 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15295 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15296 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15298 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15299 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15300 this.touchViewBodyEl.setStyle('overflow', 'auto');
15302 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15303 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15305 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15306 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15310 showTouchView : function()
15316 this.touchViewHeaderEl.hide();
15318 if(this.modalTitle.length){
15319 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15320 this.touchViewHeaderEl.show();
15323 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15324 this.touchViewEl.show();
15326 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15328 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15329 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15331 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15333 if(this.modalTitle.length){
15334 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15337 this.touchViewBodyEl.setHeight(bodyHeight);
15341 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15343 this.touchViewEl.addClass('in');
15346 if(this._touchViewMask){
15347 Roo.get(document.body).addClass("x-body-masked");
15348 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15349 this._touchViewMask.setStyle('z-index', 10000);
15350 this._touchViewMask.addClass('show');
15353 this.doTouchViewQuery();
15357 hideTouchView : function()
15359 this.touchViewEl.removeClass('in');
15363 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15365 this.touchViewEl.setStyle('display', 'none');
15368 if(this._touchViewMask){
15369 this._touchViewMask.removeClass('show');
15370 Roo.get(document.body).removeClass("x-body-masked");
15374 setTouchViewValue : function()
15381 Roo.each(this.tickItems, function(o){
15386 this.hideTouchView();
15389 doTouchViewQuery : function()
15398 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15402 if(!this.alwaysQuery || this.mode == 'local'){
15403 this.onTouchViewLoad();
15410 onTouchViewBeforeLoad : function(combo,opts)
15416 onTouchViewLoad : function()
15418 if(this.store.getCount() < 1){
15419 this.onTouchViewEmptyResults();
15423 this.clearTouchView();
15425 var rawValue = this.getRawValue();
15427 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15429 this.tickItems = [];
15431 this.store.data.each(function(d, rowIndex){
15432 var row = this.touchViewListGroup.createChild(template);
15434 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15435 row.addClass(d.data.cls);
15438 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15441 html : d.data[this.displayField]
15444 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15445 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15448 row.removeClass('selected');
15449 if(!this.multiple && this.valueField &&
15450 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15453 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15454 row.addClass('selected');
15457 if(this.multiple && this.valueField &&
15458 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15462 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15463 this.tickItems.push(d.data);
15466 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15470 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15472 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15474 if(this.modalTitle.length){
15475 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15478 var listHeight = this.touchViewListGroup.getHeight();
15480 if(this.mobile_restrict_height && listHeight < bodyHeight){
15481 this.touchViewBodyEl.setHeight(listHeight);
15486 if(firstChecked && listHeight > bodyHeight){
15487 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15492 onTouchViewLoadException : function()
15494 this.hideTouchView();
15497 onTouchViewEmptyResults : function()
15499 this.clearTouchView();
15501 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15503 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15507 clearTouchView : function()
15509 this.touchViewListGroup.dom.innerHTML = '';
15512 onTouchViewClick : function(e, el, o)
15514 e.preventDefault();
15517 var rowIndex = o.rowIndex;
15519 var r = this.store.getAt(rowIndex);
15521 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15523 if(!this.multiple){
15524 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15525 c.dom.removeAttribute('checked');
15528 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15530 this.setFromData(r.data);
15532 var close = this.closeTriggerEl();
15538 this.hideTouchView();
15540 this.fireEvent('select', this, r, rowIndex);
15545 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15546 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15547 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15551 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15552 this.addItem(r.data);
15553 this.tickItems.push(r.data);
15557 getAutoCreateNativeIOS : function()
15560 cls: 'form-group' //input-group,
15565 cls : 'roo-ios-select'
15569 combobox.name = this.name;
15572 if (this.disabled) {
15573 combobox.disabled = true;
15576 var settings = this;
15578 ['xs','sm','md','lg'].map(function(size){
15579 if (settings[size]) {
15580 cfg.cls += ' col-' + size + '-' + settings[size];
15590 initIOSView : function()
15592 this.store.on('load', this.onIOSViewLoad, this);
15597 onIOSViewLoad : function()
15599 if(this.store.getCount() < 1){
15603 this.clearIOSView();
15605 if(this.allowBlank) {
15607 var default_text = '-- SELECT --';
15609 if(this.placeholder.length){
15610 default_text = this.placeholder;
15613 if(this.emptyTitle.length){
15614 default_text += ' - ' + this.emptyTitle + ' -';
15617 var opt = this.inputEl().createChild({
15620 html : default_text
15624 o[this.valueField] = 0;
15625 o[this.displayField] = default_text;
15627 this.ios_options.push({
15634 this.store.data.each(function(d, rowIndex){
15638 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15639 html = d.data[this.displayField];
15644 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15645 value = d.data[this.valueField];
15654 if(this.value == d.data[this.valueField]){
15655 option['selected'] = true;
15658 var opt = this.inputEl().createChild(option);
15660 this.ios_options.push({
15667 this.inputEl().on('change', function(){
15668 this.fireEvent('select', this);
15673 clearIOSView: function()
15675 this.inputEl().dom.innerHTML = '';
15677 this.ios_options = [];
15680 setIOSValue: function(v)
15684 if(!this.ios_options){
15688 Roo.each(this.ios_options, function(opts){
15690 opts.el.dom.removeAttribute('selected');
15692 if(opts.data[this.valueField] != v){
15696 opts.el.dom.setAttribute('selected', true);
15702 * @cfg {Boolean} grow
15706 * @cfg {Number} growMin
15710 * @cfg {Number} growMax
15719 Roo.apply(Roo.bootstrap.ComboBox, {
15723 cls: 'modal-header',
15745 cls: 'list-group-item',
15749 cls: 'roo-combobox-list-group-item-value'
15753 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15767 listItemCheckbox : {
15769 cls: 'list-group-item',
15773 cls: 'roo-combobox-list-group-item-value'
15777 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15793 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15798 cls: 'modal-footer',
15806 cls: 'col-xs-6 text-left',
15809 cls: 'btn btn-danger roo-touch-view-cancel',
15815 cls: 'col-xs-6 text-right',
15818 cls: 'btn btn-success roo-touch-view-ok',
15829 Roo.apply(Roo.bootstrap.ComboBox, {
15831 touchViewTemplate : {
15833 cls: 'modal fade roo-combobox-touch-view',
15837 cls: 'modal-dialog',
15838 style : 'position:fixed', // we have to fix position....
15842 cls: 'modal-content',
15844 Roo.bootstrap.ComboBox.header,
15845 Roo.bootstrap.ComboBox.body,
15846 Roo.bootstrap.ComboBox.footer
15855 * Ext JS Library 1.1.1
15856 * Copyright(c) 2006-2007, Ext JS, LLC.
15858 * Originally Released Under LGPL - original licence link has changed is not relivant.
15861 * <script type="text/javascript">
15866 * @extends Roo.util.Observable
15867 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15868 * This class also supports single and multi selection modes. <br>
15869 * Create a data model bound view:
15871 var store = new Roo.data.Store(...);
15873 var view = new Roo.View({
15875 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15877 singleSelect: true,
15878 selectedClass: "ydataview-selected",
15882 // listen for node click?
15883 view.on("click", function(vw, index, node, e){
15884 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15888 dataModel.load("foobar.xml");
15890 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15892 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15893 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15895 * Note: old style constructor is still suported (container, template, config)
15898 * Create a new View
15899 * @param {Object} config The config object
15902 Roo.View = function(config, depreciated_tpl, depreciated_config){
15904 this.parent = false;
15906 if (typeof(depreciated_tpl) == 'undefined') {
15907 // new way.. - universal constructor.
15908 Roo.apply(this, config);
15909 this.el = Roo.get(this.el);
15912 this.el = Roo.get(config);
15913 this.tpl = depreciated_tpl;
15914 Roo.apply(this, depreciated_config);
15916 this.wrapEl = this.el.wrap().wrap();
15917 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15920 if(typeof(this.tpl) == "string"){
15921 this.tpl = new Roo.Template(this.tpl);
15923 // support xtype ctors..
15924 this.tpl = new Roo.factory(this.tpl, Roo);
15928 this.tpl.compile();
15933 * @event beforeclick
15934 * Fires before a click is processed. Returns false to cancel the default action.
15935 * @param {Roo.View} this
15936 * @param {Number} index The index of the target node
15937 * @param {HTMLElement} node The target node
15938 * @param {Roo.EventObject} e The raw event object
15940 "beforeclick" : true,
15943 * Fires when a template node is clicked.
15944 * @param {Roo.View} this
15945 * @param {Number} index The index of the target node
15946 * @param {HTMLElement} node The target node
15947 * @param {Roo.EventObject} e The raw event object
15952 * Fires when a template node is double clicked.
15953 * @param {Roo.View} this
15954 * @param {Number} index The index of the target node
15955 * @param {HTMLElement} node The target node
15956 * @param {Roo.EventObject} e The raw event object
15960 * @event contextmenu
15961 * Fires when a template node is right clicked.
15962 * @param {Roo.View} this
15963 * @param {Number} index The index of the target node
15964 * @param {HTMLElement} node The target node
15965 * @param {Roo.EventObject} e The raw event object
15967 "contextmenu" : true,
15969 * @event selectionchange
15970 * Fires when the selected nodes change.
15971 * @param {Roo.View} this
15972 * @param {Array} selections Array of the selected nodes
15974 "selectionchange" : true,
15977 * @event beforeselect
15978 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15979 * @param {Roo.View} this
15980 * @param {HTMLElement} node The node to be selected
15981 * @param {Array} selections Array of currently selected nodes
15983 "beforeselect" : true,
15985 * @event preparedata
15986 * Fires on every row to render, to allow you to change the data.
15987 * @param {Roo.View} this
15988 * @param {Object} data to be rendered (change this)
15990 "preparedata" : true
15998 "click": this.onClick,
15999 "dblclick": this.onDblClick,
16000 "contextmenu": this.onContextMenu,
16004 this.selections = [];
16006 this.cmp = new Roo.CompositeElementLite([]);
16008 this.store = Roo.factory(this.store, Roo.data);
16009 this.setStore(this.store, true);
16012 if ( this.footer && this.footer.xtype) {
16014 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16016 this.footer.dataSource = this.store;
16017 this.footer.container = fctr;
16018 this.footer = Roo.factory(this.footer, Roo);
16019 fctr.insertFirst(this.el);
16021 // this is a bit insane - as the paging toolbar seems to detach the el..
16022 // dom.parentNode.parentNode.parentNode
16023 // they get detached?
16027 Roo.View.superclass.constructor.call(this);
16032 Roo.extend(Roo.View, Roo.util.Observable, {
16035 * @cfg {Roo.data.Store} store Data store to load data from.
16040 * @cfg {String|Roo.Element} el The container element.
16045 * @cfg {String|Roo.Template} tpl The template used by this View
16049 * @cfg {String} dataName the named area of the template to use as the data area
16050 * Works with domtemplates roo-name="name"
16054 * @cfg {String} selectedClass The css class to add to selected nodes
16056 selectedClass : "x-view-selected",
16058 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16063 * @cfg {String} text to display on mask (default Loading)
16067 * @cfg {Boolean} multiSelect Allow multiple selection
16069 multiSelect : false,
16071 * @cfg {Boolean} singleSelect Allow single selection
16073 singleSelect: false,
16076 * @cfg {Boolean} toggleSelect - selecting
16078 toggleSelect : false,
16081 * @cfg {Boolean} tickable - selecting
16086 * Returns the element this view is bound to.
16087 * @return {Roo.Element}
16089 getEl : function(){
16090 return this.wrapEl;
16096 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16098 refresh : function(){
16099 //Roo.log('refresh');
16102 // if we are using something like 'domtemplate', then
16103 // the what gets used is:
16104 // t.applySubtemplate(NAME, data, wrapping data..)
16105 // the outer template then get' applied with
16106 // the store 'extra data'
16107 // and the body get's added to the
16108 // roo-name="data" node?
16109 // <span class='roo-tpl-{name}'></span> ?????
16113 this.clearSelections();
16114 this.el.update("");
16116 var records = this.store.getRange();
16117 if(records.length < 1) {
16119 // is this valid?? = should it render a template??
16121 this.el.update(this.emptyText);
16125 if (this.dataName) {
16126 this.el.update(t.apply(this.store.meta)); //????
16127 el = this.el.child('.roo-tpl-' + this.dataName);
16130 for(var i = 0, len = records.length; i < len; i++){
16131 var data = this.prepareData(records[i].data, i, records[i]);
16132 this.fireEvent("preparedata", this, data, i, records[i]);
16134 var d = Roo.apply({}, data);
16137 Roo.apply(d, {'roo-id' : Roo.id()});
16141 Roo.each(this.parent.item, function(item){
16142 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16145 Roo.apply(d, {'roo-data-checked' : 'checked'});
16149 html[html.length] = Roo.util.Format.trim(
16151 t.applySubtemplate(this.dataName, d, this.store.meta) :
16158 el.update(html.join(""));
16159 this.nodes = el.dom.childNodes;
16160 this.updateIndexes(0);
16165 * Function to override to reformat the data that is sent to
16166 * the template for each node.
16167 * DEPRICATED - use the preparedata event handler.
16168 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16169 * a JSON object for an UpdateManager bound view).
16171 prepareData : function(data, index, record)
16173 this.fireEvent("preparedata", this, data, index, record);
16177 onUpdate : function(ds, record){
16178 // Roo.log('on update');
16179 this.clearSelections();
16180 var index = this.store.indexOf(record);
16181 var n = this.nodes[index];
16182 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16183 n.parentNode.removeChild(n);
16184 this.updateIndexes(index, index);
16190 onAdd : function(ds, records, index)
16192 //Roo.log(['on Add', ds, records, index] );
16193 this.clearSelections();
16194 if(this.nodes.length == 0){
16198 var n = this.nodes[index];
16199 for(var i = 0, len = records.length; i < len; i++){
16200 var d = this.prepareData(records[i].data, i, records[i]);
16202 this.tpl.insertBefore(n, d);
16205 this.tpl.append(this.el, d);
16208 this.updateIndexes(index);
16211 onRemove : function(ds, record, index){
16212 // Roo.log('onRemove');
16213 this.clearSelections();
16214 var el = this.dataName ?
16215 this.el.child('.roo-tpl-' + this.dataName) :
16218 el.dom.removeChild(this.nodes[index]);
16219 this.updateIndexes(index);
16223 * Refresh an individual node.
16224 * @param {Number} index
16226 refreshNode : function(index){
16227 this.onUpdate(this.store, this.store.getAt(index));
16230 updateIndexes : function(startIndex, endIndex){
16231 var ns = this.nodes;
16232 startIndex = startIndex || 0;
16233 endIndex = endIndex || ns.length - 1;
16234 for(var i = startIndex; i <= endIndex; i++){
16235 ns[i].nodeIndex = i;
16240 * Changes the data store this view uses and refresh the view.
16241 * @param {Store} store
16243 setStore : function(store, initial){
16244 if(!initial && this.store){
16245 this.store.un("datachanged", this.refresh);
16246 this.store.un("add", this.onAdd);
16247 this.store.un("remove", this.onRemove);
16248 this.store.un("update", this.onUpdate);
16249 this.store.un("clear", this.refresh);
16250 this.store.un("beforeload", this.onBeforeLoad);
16251 this.store.un("load", this.onLoad);
16252 this.store.un("loadexception", this.onLoad);
16256 store.on("datachanged", this.refresh, this);
16257 store.on("add", this.onAdd, this);
16258 store.on("remove", this.onRemove, this);
16259 store.on("update", this.onUpdate, this);
16260 store.on("clear", this.refresh, this);
16261 store.on("beforeload", this.onBeforeLoad, this);
16262 store.on("load", this.onLoad, this);
16263 store.on("loadexception", this.onLoad, this);
16271 * onbeforeLoad - masks the loading area.
16274 onBeforeLoad : function(store,opts)
16276 //Roo.log('onBeforeLoad');
16278 this.el.update("");
16280 this.el.mask(this.mask ? this.mask : "Loading" );
16282 onLoad : function ()
16289 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16290 * @param {HTMLElement} node
16291 * @return {HTMLElement} The template node
16293 findItemFromChild : function(node){
16294 var el = this.dataName ?
16295 this.el.child('.roo-tpl-' + this.dataName,true) :
16298 if(!node || node.parentNode == el){
16301 var p = node.parentNode;
16302 while(p && p != el){
16303 if(p.parentNode == el){
16312 onClick : function(e){
16313 var item = this.findItemFromChild(e.getTarget());
16315 var index = this.indexOf(item);
16316 if(this.onItemClick(item, index, e) !== false){
16317 this.fireEvent("click", this, index, item, e);
16320 this.clearSelections();
16325 onContextMenu : function(e){
16326 var item = this.findItemFromChild(e.getTarget());
16328 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16333 onDblClick : function(e){
16334 var item = this.findItemFromChild(e.getTarget());
16336 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16340 onItemClick : function(item, index, e)
16342 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16345 if (this.toggleSelect) {
16346 var m = this.isSelected(item) ? 'unselect' : 'select';
16349 _t[m](item, true, false);
16352 if(this.multiSelect || this.singleSelect){
16353 if(this.multiSelect && e.shiftKey && this.lastSelection){
16354 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16356 this.select(item, this.multiSelect && e.ctrlKey);
16357 this.lastSelection = item;
16360 if(!this.tickable){
16361 e.preventDefault();
16369 * Get the number of selected nodes.
16372 getSelectionCount : function(){
16373 return this.selections.length;
16377 * Get the currently selected nodes.
16378 * @return {Array} An array of HTMLElements
16380 getSelectedNodes : function(){
16381 return this.selections;
16385 * Get the indexes of the selected nodes.
16388 getSelectedIndexes : function(){
16389 var indexes = [], s = this.selections;
16390 for(var i = 0, len = s.length; i < len; i++){
16391 indexes.push(s[i].nodeIndex);
16397 * Clear all selections
16398 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16400 clearSelections : function(suppressEvent){
16401 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16402 this.cmp.elements = this.selections;
16403 this.cmp.removeClass(this.selectedClass);
16404 this.selections = [];
16405 if(!suppressEvent){
16406 this.fireEvent("selectionchange", this, this.selections);
16412 * Returns true if the passed node is selected
16413 * @param {HTMLElement/Number} node The node or node index
16414 * @return {Boolean}
16416 isSelected : function(node){
16417 var s = this.selections;
16421 node = this.getNode(node);
16422 return s.indexOf(node) !== -1;
16427 * @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
16428 * @param {Boolean} keepExisting (optional) true to keep existing selections
16429 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16431 select : function(nodeInfo, keepExisting, suppressEvent){
16432 if(nodeInfo instanceof Array){
16434 this.clearSelections(true);
16436 for(var i = 0, len = nodeInfo.length; i < len; i++){
16437 this.select(nodeInfo[i], true, true);
16441 var node = this.getNode(nodeInfo);
16442 if(!node || this.isSelected(node)){
16443 return; // already selected.
16446 this.clearSelections(true);
16449 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16450 Roo.fly(node).addClass(this.selectedClass);
16451 this.selections.push(node);
16452 if(!suppressEvent){
16453 this.fireEvent("selectionchange", this, this.selections);
16461 * @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
16462 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16463 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16465 unselect : function(nodeInfo, keepExisting, suppressEvent)
16467 if(nodeInfo instanceof Array){
16468 Roo.each(this.selections, function(s) {
16469 this.unselect(s, nodeInfo);
16473 var node = this.getNode(nodeInfo);
16474 if(!node || !this.isSelected(node)){
16475 //Roo.log("not selected");
16476 return; // not selected.
16480 Roo.each(this.selections, function(s) {
16482 Roo.fly(node).removeClass(this.selectedClass);
16489 this.selections= ns;
16490 this.fireEvent("selectionchange", this, this.selections);
16494 * Gets a template node.
16495 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16496 * @return {HTMLElement} The node or null if it wasn't found
16498 getNode : function(nodeInfo){
16499 if(typeof nodeInfo == "string"){
16500 return document.getElementById(nodeInfo);
16501 }else if(typeof nodeInfo == "number"){
16502 return this.nodes[nodeInfo];
16508 * Gets a range template nodes.
16509 * @param {Number} startIndex
16510 * @param {Number} endIndex
16511 * @return {Array} An array of nodes
16513 getNodes : function(start, end){
16514 var ns = this.nodes;
16515 start = start || 0;
16516 end = typeof end == "undefined" ? ns.length - 1 : end;
16519 for(var i = start; i <= end; i++){
16523 for(var i = start; i >= end; i--){
16531 * Finds the index of the passed node
16532 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16533 * @return {Number} The index of the node or -1
16535 indexOf : function(node){
16536 node = this.getNode(node);
16537 if(typeof node.nodeIndex == "number"){
16538 return node.nodeIndex;
16540 var ns = this.nodes;
16541 for(var i = 0, len = ns.length; i < len; i++){
16552 * based on jquery fullcalendar
16556 Roo.bootstrap = Roo.bootstrap || {};
16558 * @class Roo.bootstrap.Calendar
16559 * @extends Roo.bootstrap.Component
16560 * Bootstrap Calendar class
16561 * @cfg {Boolean} loadMask (true|false) default false
16562 * @cfg {Object} header generate the user specific header of the calendar, default false
16565 * Create a new Container
16566 * @param {Object} config The config object
16571 Roo.bootstrap.Calendar = function(config){
16572 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16576 * Fires when a date is selected
16577 * @param {DatePicker} this
16578 * @param {Date} date The selected date
16582 * @event monthchange
16583 * Fires when the displayed month changes
16584 * @param {DatePicker} this
16585 * @param {Date} date The selected month
16587 'monthchange': true,
16589 * @event evententer
16590 * Fires when mouse over an event
16591 * @param {Calendar} this
16592 * @param {event} Event
16594 'evententer': true,
16596 * @event eventleave
16597 * Fires when the mouse leaves an
16598 * @param {Calendar} this
16601 'eventleave': true,
16603 * @event eventclick
16604 * Fires when the mouse click an
16605 * @param {Calendar} this
16614 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16617 * @cfg {Number} startDay
16618 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16626 getAutoCreate : function(){
16629 var fc_button = function(name, corner, style, content ) {
16630 return Roo.apply({},{
16632 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16634 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16637 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16648 style : 'width:100%',
16655 cls : 'fc-header-left',
16657 fc_button('prev', 'left', 'arrow', '‹' ),
16658 fc_button('next', 'right', 'arrow', '›' ),
16659 { tag: 'span', cls: 'fc-header-space' },
16660 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16668 cls : 'fc-header-center',
16672 cls: 'fc-header-title',
16675 html : 'month / year'
16683 cls : 'fc-header-right',
16685 /* fc_button('month', 'left', '', 'month' ),
16686 fc_button('week', '', '', 'week' ),
16687 fc_button('day', 'right', '', 'day' )
16699 header = this.header;
16702 var cal_heads = function() {
16704 // fixme - handle this.
16706 for (var i =0; i < Date.dayNames.length; i++) {
16707 var d = Date.dayNames[i];
16710 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16711 html : d.substring(0,3)
16715 ret[0].cls += ' fc-first';
16716 ret[6].cls += ' fc-last';
16719 var cal_cell = function(n) {
16722 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16727 cls: 'fc-day-number',
16731 cls: 'fc-day-content',
16735 style: 'position: relative;' // height: 17px;
16747 var cal_rows = function() {
16750 for (var r = 0; r < 6; r++) {
16757 for (var i =0; i < Date.dayNames.length; i++) {
16758 var d = Date.dayNames[i];
16759 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16762 row.cn[0].cls+=' fc-first';
16763 row.cn[0].cn[0].style = 'min-height:90px';
16764 row.cn[6].cls+=' fc-last';
16768 ret[0].cls += ' fc-first';
16769 ret[4].cls += ' fc-prev-last';
16770 ret[5].cls += ' fc-last';
16777 cls: 'fc-border-separate',
16778 style : 'width:100%',
16786 cls : 'fc-first fc-last',
16804 cls : 'fc-content',
16805 style : "position: relative;",
16808 cls : 'fc-view fc-view-month fc-grid',
16809 style : 'position: relative',
16810 unselectable : 'on',
16813 cls : 'fc-event-container',
16814 style : 'position:absolute;z-index:8;top:0;left:0;'
16832 initEvents : function()
16835 throw "can not find store for calendar";
16841 style: "text-align:center",
16845 style: "background-color:white;width:50%;margin:250 auto",
16849 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16860 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16862 var size = this.el.select('.fc-content', true).first().getSize();
16863 this.maskEl.setSize(size.width, size.height);
16864 this.maskEl.enableDisplayMode("block");
16865 if(!this.loadMask){
16866 this.maskEl.hide();
16869 this.store = Roo.factory(this.store, Roo.data);
16870 this.store.on('load', this.onLoad, this);
16871 this.store.on('beforeload', this.onBeforeLoad, this);
16875 this.cells = this.el.select('.fc-day',true);
16876 //Roo.log(this.cells);
16877 this.textNodes = this.el.query('.fc-day-number');
16878 this.cells.addClassOnOver('fc-state-hover');
16880 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16881 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16882 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16883 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16885 this.on('monthchange', this.onMonthChange, this);
16887 this.update(new Date().clearTime());
16890 resize : function() {
16891 var sz = this.el.getSize();
16893 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16894 this.el.select('.fc-day-content div',true).setHeight(34);
16899 showPrevMonth : function(e){
16900 this.update(this.activeDate.add("mo", -1));
16902 showToday : function(e){
16903 this.update(new Date().clearTime());
16906 showNextMonth : function(e){
16907 this.update(this.activeDate.add("mo", 1));
16911 showPrevYear : function(){
16912 this.update(this.activeDate.add("y", -1));
16916 showNextYear : function(){
16917 this.update(this.activeDate.add("y", 1));
16922 update : function(date)
16924 var vd = this.activeDate;
16925 this.activeDate = date;
16926 // if(vd && this.el){
16927 // var t = date.getTime();
16928 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16929 // Roo.log('using add remove');
16931 // this.fireEvent('monthchange', this, date);
16933 // this.cells.removeClass("fc-state-highlight");
16934 // this.cells.each(function(c){
16935 // if(c.dateValue == t){
16936 // c.addClass("fc-state-highlight");
16937 // setTimeout(function(){
16938 // try{c.dom.firstChild.focus();}catch(e){}
16948 var days = date.getDaysInMonth();
16950 var firstOfMonth = date.getFirstDateOfMonth();
16951 var startingPos = firstOfMonth.getDay()-this.startDay;
16953 if(startingPos < this.startDay){
16957 var pm = date.add(Date.MONTH, -1);
16958 var prevStart = pm.getDaysInMonth()-startingPos;
16960 this.cells = this.el.select('.fc-day',true);
16961 this.textNodes = this.el.query('.fc-day-number');
16962 this.cells.addClassOnOver('fc-state-hover');
16964 var cells = this.cells.elements;
16965 var textEls = this.textNodes;
16967 Roo.each(cells, function(cell){
16968 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16971 days += startingPos;
16973 // convert everything to numbers so it's fast
16974 var day = 86400000;
16975 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16978 //Roo.log(prevStart);
16980 var today = new Date().clearTime().getTime();
16981 var sel = date.clearTime().getTime();
16982 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16983 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16984 var ddMatch = this.disabledDatesRE;
16985 var ddText = this.disabledDatesText;
16986 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16987 var ddaysText = this.disabledDaysText;
16988 var format = this.format;
16990 var setCellClass = function(cal, cell){
16994 //Roo.log('set Cell Class');
16996 var t = d.getTime();
17000 cell.dateValue = t;
17002 cell.className += " fc-today";
17003 cell.className += " fc-state-highlight";
17004 cell.title = cal.todayText;
17007 // disable highlight in other month..
17008 //cell.className += " fc-state-highlight";
17013 cell.className = " fc-state-disabled";
17014 cell.title = cal.minText;
17018 cell.className = " fc-state-disabled";
17019 cell.title = cal.maxText;
17023 if(ddays.indexOf(d.getDay()) != -1){
17024 cell.title = ddaysText;
17025 cell.className = " fc-state-disabled";
17028 if(ddMatch && format){
17029 var fvalue = d.dateFormat(format);
17030 if(ddMatch.test(fvalue)){
17031 cell.title = ddText.replace("%0", fvalue);
17032 cell.className = " fc-state-disabled";
17036 if (!cell.initialClassName) {
17037 cell.initialClassName = cell.dom.className;
17040 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17045 for(; i < startingPos; i++) {
17046 textEls[i].innerHTML = (++prevStart);
17047 d.setDate(d.getDate()+1);
17049 cells[i].className = "fc-past fc-other-month";
17050 setCellClass(this, cells[i]);
17055 for(; i < days; i++){
17056 intDay = i - startingPos + 1;
17057 textEls[i].innerHTML = (intDay);
17058 d.setDate(d.getDate()+1);
17060 cells[i].className = ''; // "x-date-active";
17061 setCellClass(this, cells[i]);
17065 for(; i < 42; i++) {
17066 textEls[i].innerHTML = (++extraDays);
17067 d.setDate(d.getDate()+1);
17069 cells[i].className = "fc-future fc-other-month";
17070 setCellClass(this, cells[i]);
17073 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17075 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17077 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17078 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17080 if(totalRows != 6){
17081 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17082 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17085 this.fireEvent('monthchange', this, date);
17089 if(!this.internalRender){
17090 var main = this.el.dom.firstChild;
17091 var w = main.offsetWidth;
17092 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17093 Roo.fly(main).setWidth(w);
17094 this.internalRender = true;
17095 // opera does not respect the auto grow header center column
17096 // then, after it gets a width opera refuses to recalculate
17097 // without a second pass
17098 if(Roo.isOpera && !this.secondPass){
17099 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17100 this.secondPass = true;
17101 this.update.defer(10, this, [date]);
17108 findCell : function(dt) {
17109 dt = dt.clearTime().getTime();
17111 this.cells.each(function(c){
17112 //Roo.log("check " +c.dateValue + '?=' + dt);
17113 if(c.dateValue == dt){
17123 findCells : function(ev) {
17124 var s = ev.start.clone().clearTime().getTime();
17126 var e= ev.end.clone().clearTime().getTime();
17129 this.cells.each(function(c){
17130 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17132 if(c.dateValue > e){
17135 if(c.dateValue < s){
17144 // findBestRow: function(cells)
17148 // for (var i =0 ; i < cells.length;i++) {
17149 // ret = Math.max(cells[i].rows || 0,ret);
17156 addItem : function(ev)
17158 // look for vertical location slot in
17159 var cells = this.findCells(ev);
17161 // ev.row = this.findBestRow(cells);
17163 // work out the location.
17167 for(var i =0; i < cells.length; i++) {
17169 cells[i].row = cells[0].row;
17172 cells[i].row = cells[i].row + 1;
17182 if (crow.start.getY() == cells[i].getY()) {
17184 crow.end = cells[i];
17201 cells[0].events.push(ev);
17203 this.calevents.push(ev);
17206 clearEvents: function() {
17208 if(!this.calevents){
17212 Roo.each(this.cells.elements, function(c){
17218 Roo.each(this.calevents, function(e) {
17219 Roo.each(e.els, function(el) {
17220 el.un('mouseenter' ,this.onEventEnter, this);
17221 el.un('mouseleave' ,this.onEventLeave, this);
17226 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17232 renderEvents: function()
17236 this.cells.each(function(c) {
17245 if(c.row != c.events.length){
17246 r = 4 - (4 - (c.row - c.events.length));
17249 c.events = ev.slice(0, r);
17250 c.more = ev.slice(r);
17252 if(c.more.length && c.more.length == 1){
17253 c.events.push(c.more.pop());
17256 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17260 this.cells.each(function(c) {
17262 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17265 for (var e = 0; e < c.events.length; e++){
17266 var ev = c.events[e];
17267 var rows = ev.rows;
17269 for(var i = 0; i < rows.length; i++) {
17271 // how many rows should it span..
17274 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17275 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17277 unselectable : "on",
17280 cls: 'fc-event-inner',
17284 // cls: 'fc-event-time',
17285 // html : cells.length > 1 ? '' : ev.time
17289 cls: 'fc-event-title',
17290 html : String.format('{0}', ev.title)
17297 cls: 'ui-resizable-handle ui-resizable-e',
17298 html : '  '
17305 cfg.cls += ' fc-event-start';
17307 if ((i+1) == rows.length) {
17308 cfg.cls += ' fc-event-end';
17311 var ctr = _this.el.select('.fc-event-container',true).first();
17312 var cg = ctr.createChild(cfg);
17314 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17315 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17317 var r = (c.more.length) ? 1 : 0;
17318 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17319 cg.setWidth(ebox.right - sbox.x -2);
17321 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17322 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17323 cg.on('click', _this.onEventClick, _this, ev);
17334 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17335 style : 'position: absolute',
17336 unselectable : "on",
17339 cls: 'fc-event-inner',
17343 cls: 'fc-event-title',
17351 cls: 'ui-resizable-handle ui-resizable-e',
17352 html : '  '
17358 var ctr = _this.el.select('.fc-event-container',true).first();
17359 var cg = ctr.createChild(cfg);
17361 var sbox = c.select('.fc-day-content',true).first().getBox();
17362 var ebox = c.select('.fc-day-content',true).first().getBox();
17364 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17365 cg.setWidth(ebox.right - sbox.x -2);
17367 cg.on('click', _this.onMoreEventClick, _this, c.more);
17377 onEventEnter: function (e, el,event,d) {
17378 this.fireEvent('evententer', this, el, event);
17381 onEventLeave: function (e, el,event,d) {
17382 this.fireEvent('eventleave', this, el, event);
17385 onEventClick: function (e, el,event,d) {
17386 this.fireEvent('eventclick', this, el, event);
17389 onMonthChange: function () {
17393 onMoreEventClick: function(e, el, more)
17397 this.calpopover.placement = 'right';
17398 this.calpopover.setTitle('More');
17400 this.calpopover.setContent('');
17402 var ctr = this.calpopover.el.select('.popover-content', true).first();
17404 Roo.each(more, function(m){
17406 cls : 'fc-event-hori fc-event-draggable',
17409 var cg = ctr.createChild(cfg);
17411 cg.on('click', _this.onEventClick, _this, m);
17414 this.calpopover.show(el);
17419 onLoad: function ()
17421 this.calevents = [];
17424 if(this.store.getCount() > 0){
17425 this.store.data.each(function(d){
17428 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17429 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17430 time : d.data.start_time,
17431 title : d.data.title,
17432 description : d.data.description,
17433 venue : d.data.venue
17438 this.renderEvents();
17440 if(this.calevents.length && this.loadMask){
17441 this.maskEl.hide();
17445 onBeforeLoad: function()
17447 this.clearEvents();
17449 this.maskEl.show();
17463 * @class Roo.bootstrap.Popover
17464 * @extends Roo.bootstrap.Component
17465 * Bootstrap Popover class
17466 * @cfg {String} html contents of the popover (or false to use children..)
17467 * @cfg {String} title of popover (or false to hide)
17468 * @cfg {String} placement how it is placed
17469 * @cfg {String} trigger click || hover (or false to trigger manually)
17470 * @cfg {String} over what (parent or false to trigger manually.)
17471 * @cfg {Number} delay - delay before showing
17474 * Create a new Popover
17475 * @param {Object} config The config object
17478 Roo.bootstrap.Popover = function(config){
17479 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17485 * After the popover show
17487 * @param {Roo.bootstrap.Popover} this
17492 * After the popover hide
17494 * @param {Roo.bootstrap.Popover} this
17500 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17502 title: 'Fill in a title',
17505 placement : 'right',
17506 trigger : 'hover', // hover
17512 can_build_overlaid : false,
17514 getChildContainer : function()
17516 return this.el.select('.popover-content',true).first();
17519 getAutoCreate : function(){
17522 cls : 'popover roo-dynamic',
17523 style: 'display:block',
17529 cls : 'popover-inner',
17533 cls: 'popover-title',
17537 cls : 'popover-content',
17548 setTitle: function(str)
17551 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17553 setContent: function(str)
17556 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17558 // as it get's added to the bottom of the page.
17559 onRender : function(ct, position)
17561 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17563 var cfg = Roo.apply({}, this.getAutoCreate());
17567 cfg.cls += ' ' + this.cls;
17570 cfg.style = this.style;
17572 //Roo.log("adding to ");
17573 this.el = Roo.get(document.body).createChild(cfg, position);
17574 // Roo.log(this.el);
17579 initEvents : function()
17581 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17582 this.el.enableDisplayMode('block');
17584 if (this.over === false) {
17587 if (this.triggers === false) {
17590 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17591 var triggers = this.trigger ? this.trigger.split(' ') : [];
17592 Roo.each(triggers, function(trigger) {
17594 if (trigger == 'click') {
17595 on_el.on('click', this.toggle, this);
17596 } else if (trigger != 'manual') {
17597 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17598 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17600 on_el.on(eventIn ,this.enter, this);
17601 on_el.on(eventOut, this.leave, this);
17612 toggle : function () {
17613 this.hoverState == 'in' ? this.leave() : this.enter();
17616 enter : function () {
17618 clearTimeout(this.timeout);
17620 this.hoverState = 'in';
17622 if (!this.delay || !this.delay.show) {
17627 this.timeout = setTimeout(function () {
17628 if (_t.hoverState == 'in') {
17631 }, this.delay.show)
17634 leave : function() {
17635 clearTimeout(this.timeout);
17637 this.hoverState = 'out';
17639 if (!this.delay || !this.delay.hide) {
17644 this.timeout = setTimeout(function () {
17645 if (_t.hoverState == 'out') {
17648 }, this.delay.hide)
17651 show : function (on_el)
17654 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17658 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17659 if (this.html !== false) {
17660 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17662 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17663 if (!this.title.length) {
17664 this.el.select('.popover-title',true).hide();
17667 var placement = typeof this.placement == 'function' ?
17668 this.placement.call(this, this.el, on_el) :
17671 var autoToken = /\s?auto?\s?/i;
17672 var autoPlace = autoToken.test(placement);
17674 placement = placement.replace(autoToken, '') || 'top';
17678 //this.el.setXY([0,0]);
17680 this.el.dom.style.display='block';
17681 this.el.addClass(placement);
17683 //this.el.appendTo(on_el);
17685 var p = this.getPosition();
17686 var box = this.el.getBox();
17691 var align = Roo.bootstrap.Popover.alignment[placement];
17694 this.el.alignTo(on_el, align[0],align[1]);
17695 //var arrow = this.el.select('.arrow',true).first();
17696 //arrow.set(align[2],
17698 this.el.addClass('in');
17701 if (this.el.hasClass('fade')) {
17705 this.hoverState = 'in';
17707 this.fireEvent('show', this);
17712 this.el.setXY([0,0]);
17713 this.el.removeClass('in');
17715 this.hoverState = null;
17717 this.fireEvent('hide', this);
17722 Roo.bootstrap.Popover.alignment = {
17723 'left' : ['r-l', [-10,0], 'right'],
17724 'right' : ['l-r', [10,0], 'left'],
17725 'bottom' : ['t-b', [0,10], 'top'],
17726 'top' : [ 'b-t', [0,-10], 'bottom']
17737 * @class Roo.bootstrap.Progress
17738 * @extends Roo.bootstrap.Component
17739 * Bootstrap Progress class
17740 * @cfg {Boolean} striped striped of the progress bar
17741 * @cfg {Boolean} active animated of the progress bar
17745 * Create a new Progress
17746 * @param {Object} config The config object
17749 Roo.bootstrap.Progress = function(config){
17750 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17753 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17758 getAutoCreate : function(){
17766 cfg.cls += ' progress-striped';
17770 cfg.cls += ' active';
17789 * @class Roo.bootstrap.ProgressBar
17790 * @extends Roo.bootstrap.Component
17791 * Bootstrap ProgressBar class
17792 * @cfg {Number} aria_valuenow aria-value now
17793 * @cfg {Number} aria_valuemin aria-value min
17794 * @cfg {Number} aria_valuemax aria-value max
17795 * @cfg {String} label label for the progress bar
17796 * @cfg {String} panel (success | info | warning | danger )
17797 * @cfg {String} role role of the progress bar
17798 * @cfg {String} sr_only text
17802 * Create a new ProgressBar
17803 * @param {Object} config The config object
17806 Roo.bootstrap.ProgressBar = function(config){
17807 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17810 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17814 aria_valuemax : 100,
17820 getAutoCreate : function()
17825 cls: 'progress-bar',
17826 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17838 cfg.role = this.role;
17841 if(this.aria_valuenow){
17842 cfg['aria-valuenow'] = this.aria_valuenow;
17845 if(this.aria_valuemin){
17846 cfg['aria-valuemin'] = this.aria_valuemin;
17849 if(this.aria_valuemax){
17850 cfg['aria-valuemax'] = this.aria_valuemax;
17853 if(this.label && !this.sr_only){
17854 cfg.html = this.label;
17858 cfg.cls += ' progress-bar-' + this.panel;
17864 update : function(aria_valuenow)
17866 this.aria_valuenow = aria_valuenow;
17868 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17883 * @class Roo.bootstrap.TabGroup
17884 * @extends Roo.bootstrap.Column
17885 * Bootstrap Column class
17886 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17887 * @cfg {Boolean} carousel true to make the group behave like a carousel
17888 * @cfg {Boolean} bullets show bullets for the panels
17889 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17890 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17891 * @cfg {Boolean} showarrow (true|false) show arrow default true
17894 * Create a new TabGroup
17895 * @param {Object} config The config object
17898 Roo.bootstrap.TabGroup = function(config){
17899 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17901 this.navId = Roo.id();
17904 Roo.bootstrap.TabGroup.register(this);
17908 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17911 transition : false,
17916 slideOnTouch : false,
17919 getAutoCreate : function()
17921 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17923 cfg.cls += ' tab-content';
17925 if (this.carousel) {
17926 cfg.cls += ' carousel slide';
17929 cls : 'carousel-inner',
17933 if(this.bullets && !Roo.isTouch){
17936 cls : 'carousel-bullets',
17940 if(this.bullets_cls){
17941 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17948 cfg.cn[0].cn.push(bullets);
17951 if(this.showarrow){
17952 cfg.cn[0].cn.push({
17954 class : 'carousel-arrow',
17958 class : 'carousel-prev',
17962 class : 'fa fa-chevron-left'
17968 class : 'carousel-next',
17972 class : 'fa fa-chevron-right'
17985 initEvents: function()
17987 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17988 // this.el.on("touchstart", this.onTouchStart, this);
17991 if(this.autoslide){
17994 this.slideFn = window.setInterval(function() {
17995 _this.showPanelNext();
17999 if(this.showarrow){
18000 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18001 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18007 // onTouchStart : function(e, el, o)
18009 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18013 // this.showPanelNext();
18017 getChildContainer : function()
18019 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18023 * register a Navigation item
18024 * @param {Roo.bootstrap.NavItem} the navitem to add
18026 register : function(item)
18028 this.tabs.push( item);
18029 item.navId = this.navId; // not really needed..
18034 getActivePanel : function()
18037 Roo.each(this.tabs, function(t) {
18047 getPanelByName : function(n)
18050 Roo.each(this.tabs, function(t) {
18051 if (t.tabId == n) {
18059 indexOfPanel : function(p)
18062 Roo.each(this.tabs, function(t,i) {
18063 if (t.tabId == p.tabId) {
18072 * show a specific panel
18073 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18074 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18076 showPanel : function (pan)
18078 if(this.transition || typeof(pan) == 'undefined'){
18079 Roo.log("waiting for the transitionend");
18083 if (typeof(pan) == 'number') {
18084 pan = this.tabs[pan];
18087 if (typeof(pan) == 'string') {
18088 pan = this.getPanelByName(pan);
18091 var cur = this.getActivePanel();
18094 Roo.log('pan or acitve pan is undefined');
18098 if (pan.tabId == this.getActivePanel().tabId) {
18102 if (false === cur.fireEvent('beforedeactivate')) {
18106 if(this.bullets > 0 && !Roo.isTouch){
18107 this.setActiveBullet(this.indexOfPanel(pan));
18110 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18112 this.transition = true;
18113 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18114 var lr = dir == 'next' ? 'left' : 'right';
18115 pan.el.addClass(dir); // or prev
18116 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18117 cur.el.addClass(lr); // or right
18118 pan.el.addClass(lr);
18121 cur.el.on('transitionend', function() {
18122 Roo.log("trans end?");
18124 pan.el.removeClass([lr,dir]);
18125 pan.setActive(true);
18127 cur.el.removeClass([lr]);
18128 cur.setActive(false);
18130 _this.transition = false;
18132 }, this, { single: true } );
18137 cur.setActive(false);
18138 pan.setActive(true);
18143 showPanelNext : function()
18145 var i = this.indexOfPanel(this.getActivePanel());
18147 if (i >= this.tabs.length - 1 && !this.autoslide) {
18151 if (i >= this.tabs.length - 1 && this.autoslide) {
18155 this.showPanel(this.tabs[i+1]);
18158 showPanelPrev : function()
18160 var i = this.indexOfPanel(this.getActivePanel());
18162 if (i < 1 && !this.autoslide) {
18166 if (i < 1 && this.autoslide) {
18167 i = this.tabs.length;
18170 this.showPanel(this.tabs[i-1]);
18174 addBullet: function()
18176 if(!this.bullets || Roo.isTouch){
18179 var ctr = this.el.select('.carousel-bullets',true).first();
18180 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18181 var bullet = ctr.createChild({
18182 cls : 'bullet bullet-' + i
18183 },ctr.dom.lastChild);
18188 bullet.on('click', (function(e, el, o, ii, t){
18190 e.preventDefault();
18192 this.showPanel(ii);
18194 if(this.autoslide && this.slideFn){
18195 clearInterval(this.slideFn);
18196 this.slideFn = window.setInterval(function() {
18197 _this.showPanelNext();
18201 }).createDelegate(this, [i, bullet], true));
18206 setActiveBullet : function(i)
18212 Roo.each(this.el.select('.bullet', true).elements, function(el){
18213 el.removeClass('selected');
18216 var bullet = this.el.select('.bullet-' + i, true).first();
18222 bullet.addClass('selected');
18233 Roo.apply(Roo.bootstrap.TabGroup, {
18237 * register a Navigation Group
18238 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18240 register : function(navgrp)
18242 this.groups[navgrp.navId] = navgrp;
18246 * fetch a Navigation Group based on the navigation ID
18247 * if one does not exist , it will get created.
18248 * @param {string} the navgroup to add
18249 * @returns {Roo.bootstrap.NavGroup} the navgroup
18251 get: function(navId) {
18252 if (typeof(this.groups[navId]) == 'undefined') {
18253 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18255 return this.groups[navId] ;
18270 * @class Roo.bootstrap.TabPanel
18271 * @extends Roo.bootstrap.Component
18272 * Bootstrap TabPanel class
18273 * @cfg {Boolean} active panel active
18274 * @cfg {String} html panel content
18275 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18276 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18277 * @cfg {String} href click to link..
18281 * Create a new TabPanel
18282 * @param {Object} config The config object
18285 Roo.bootstrap.TabPanel = function(config){
18286 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18290 * Fires when the active status changes
18291 * @param {Roo.bootstrap.TabPanel} this
18292 * @param {Boolean} state the new state
18297 * @event beforedeactivate
18298 * Fires before a tab is de-activated - can be used to do validation on a form.
18299 * @param {Roo.bootstrap.TabPanel} this
18300 * @return {Boolean} false if there is an error
18303 'beforedeactivate': true
18306 this.tabId = this.tabId || Roo.id();
18310 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18318 getAutoCreate : function(){
18321 // item is needed for carousel - not sure if it has any effect otherwise
18322 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18323 html: this.html || ''
18327 cfg.cls += ' active';
18331 cfg.tabId = this.tabId;
18338 initEvents: function()
18340 var p = this.parent();
18342 this.navId = this.navId || p.navId;
18344 if (typeof(this.navId) != 'undefined') {
18345 // not really needed.. but just in case.. parent should be a NavGroup.
18346 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18350 var i = tg.tabs.length - 1;
18352 if(this.active && tg.bullets > 0 && i < tg.bullets){
18353 tg.setActiveBullet(i);
18357 this.el.on('click', this.onClick, this);
18360 this.el.on("touchstart", this.onTouchStart, this);
18361 this.el.on("touchmove", this.onTouchMove, this);
18362 this.el.on("touchend", this.onTouchEnd, this);
18367 onRender : function(ct, position)
18369 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18372 setActive : function(state)
18374 Roo.log("panel - set active " + this.tabId + "=" + state);
18376 this.active = state;
18378 this.el.removeClass('active');
18380 } else if (!this.el.hasClass('active')) {
18381 this.el.addClass('active');
18384 this.fireEvent('changed', this, state);
18387 onClick : function(e)
18389 e.preventDefault();
18391 if(!this.href.length){
18395 window.location.href = this.href;
18404 onTouchStart : function(e)
18406 this.swiping = false;
18408 this.startX = e.browserEvent.touches[0].clientX;
18409 this.startY = e.browserEvent.touches[0].clientY;
18412 onTouchMove : function(e)
18414 this.swiping = true;
18416 this.endX = e.browserEvent.touches[0].clientX;
18417 this.endY = e.browserEvent.touches[0].clientY;
18420 onTouchEnd : function(e)
18427 var tabGroup = this.parent();
18429 if(this.endX > this.startX){ // swiping right
18430 tabGroup.showPanelPrev();
18434 if(this.startX > this.endX){ // swiping left
18435 tabGroup.showPanelNext();
18454 * @class Roo.bootstrap.DateField
18455 * @extends Roo.bootstrap.Input
18456 * Bootstrap DateField class
18457 * @cfg {Number} weekStart default 0
18458 * @cfg {String} viewMode default empty, (months|years)
18459 * @cfg {String} minViewMode default empty, (months|years)
18460 * @cfg {Number} startDate default -Infinity
18461 * @cfg {Number} endDate default Infinity
18462 * @cfg {Boolean} todayHighlight default false
18463 * @cfg {Boolean} todayBtn default false
18464 * @cfg {Boolean} calendarWeeks default false
18465 * @cfg {Object} daysOfWeekDisabled default empty
18466 * @cfg {Boolean} singleMode default false (true | false)
18468 * @cfg {Boolean} keyboardNavigation default true
18469 * @cfg {String} language default en
18472 * Create a new DateField
18473 * @param {Object} config The config object
18476 Roo.bootstrap.DateField = function(config){
18477 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18481 * Fires when this field show.
18482 * @param {Roo.bootstrap.DateField} this
18483 * @param {Mixed} date The date value
18488 * Fires when this field hide.
18489 * @param {Roo.bootstrap.DateField} this
18490 * @param {Mixed} date The date value
18495 * Fires when select a date.
18496 * @param {Roo.bootstrap.DateField} this
18497 * @param {Mixed} date The date value
18501 * @event beforeselect
18502 * Fires when before select a date.
18503 * @param {Roo.bootstrap.DateField} this
18504 * @param {Mixed} date The date value
18506 beforeselect : true
18510 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18513 * @cfg {String} format
18514 * The default date format string which can be overriden for localization support. The format must be
18515 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18519 * @cfg {String} altFormats
18520 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18521 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18523 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18531 todayHighlight : false,
18537 keyboardNavigation: true,
18539 calendarWeeks: false,
18541 startDate: -Infinity,
18545 daysOfWeekDisabled: [],
18549 singleMode : false,
18551 UTCDate: function()
18553 return new Date(Date.UTC.apply(Date, arguments));
18556 UTCToday: function()
18558 var today = new Date();
18559 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18562 getDate: function() {
18563 var d = this.getUTCDate();
18564 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18567 getUTCDate: function() {
18571 setDate: function(d) {
18572 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18575 setUTCDate: function(d) {
18577 this.setValue(this.formatDate(this.date));
18580 onRender: function(ct, position)
18583 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18585 this.language = this.language || 'en';
18586 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18587 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18589 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18590 this.format = this.format || 'm/d/y';
18591 this.isInline = false;
18592 this.isInput = true;
18593 this.component = this.el.select('.add-on', true).first() || false;
18594 this.component = (this.component && this.component.length === 0) ? false : this.component;
18595 this.hasInput = this.component && this.inputEl().length;
18597 if (typeof(this.minViewMode === 'string')) {
18598 switch (this.minViewMode) {
18600 this.minViewMode = 1;
18603 this.minViewMode = 2;
18606 this.minViewMode = 0;
18611 if (typeof(this.viewMode === 'string')) {
18612 switch (this.viewMode) {
18625 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18627 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18629 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18631 this.picker().on('mousedown', this.onMousedown, this);
18632 this.picker().on('click', this.onClick, this);
18634 this.picker().addClass('datepicker-dropdown');
18636 this.startViewMode = this.viewMode;
18638 if(this.singleMode){
18639 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18640 v.setVisibilityMode(Roo.Element.DISPLAY);
18644 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18645 v.setStyle('width', '189px');
18649 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18650 if(!this.calendarWeeks){
18655 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18656 v.attr('colspan', function(i, val){
18657 return parseInt(val) + 1;
18662 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18664 this.setStartDate(this.startDate);
18665 this.setEndDate(this.endDate);
18667 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18674 if(this.isInline) {
18679 picker : function()
18681 return this.pickerEl;
18682 // return this.el.select('.datepicker', true).first();
18685 fillDow: function()
18687 var dowCnt = this.weekStart;
18696 if(this.calendarWeeks){
18704 while (dowCnt < this.weekStart + 7) {
18708 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18712 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18715 fillMonths: function()
18718 var months = this.picker().select('>.datepicker-months td', true).first();
18720 months.dom.innerHTML = '';
18726 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18729 months.createChild(month);
18736 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;
18738 if (this.date < this.startDate) {
18739 this.viewDate = new Date(this.startDate);
18740 } else if (this.date > this.endDate) {
18741 this.viewDate = new Date(this.endDate);
18743 this.viewDate = new Date(this.date);
18751 var d = new Date(this.viewDate),
18752 year = d.getUTCFullYear(),
18753 month = d.getUTCMonth(),
18754 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18755 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18756 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18757 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18758 currentDate = this.date && this.date.valueOf(),
18759 today = this.UTCToday();
18761 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18763 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18765 // this.picker.select('>tfoot th.today').
18766 // .text(dates[this.language].today)
18767 // .toggle(this.todayBtn !== false);
18769 this.updateNavArrows();
18772 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18774 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18776 prevMonth.setUTCDate(day);
18778 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18780 var nextMonth = new Date(prevMonth);
18782 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18784 nextMonth = nextMonth.valueOf();
18786 var fillMonths = false;
18788 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18790 while(prevMonth.valueOf() <= nextMonth) {
18793 if (prevMonth.getUTCDay() === this.weekStart) {
18795 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18803 if(this.calendarWeeks){
18804 // ISO 8601: First week contains first thursday.
18805 // ISO also states week starts on Monday, but we can be more abstract here.
18807 // Start of current week: based on weekstart/current date
18808 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18809 // Thursday of this week
18810 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18811 // First Thursday of year, year from thursday
18812 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18813 // Calendar week: ms between thursdays, div ms per day, div 7 days
18814 calWeek = (th - yth) / 864e5 / 7 + 1;
18816 fillMonths.cn.push({
18824 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18826 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18829 if (this.todayHighlight &&
18830 prevMonth.getUTCFullYear() == today.getFullYear() &&
18831 prevMonth.getUTCMonth() == today.getMonth() &&
18832 prevMonth.getUTCDate() == today.getDate()) {
18833 clsName += ' today';
18836 if (currentDate && prevMonth.valueOf() === currentDate) {
18837 clsName += ' active';
18840 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18841 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18842 clsName += ' disabled';
18845 fillMonths.cn.push({
18847 cls: 'day ' + clsName,
18848 html: prevMonth.getDate()
18851 prevMonth.setDate(prevMonth.getDate()+1);
18854 var currentYear = this.date && this.date.getUTCFullYear();
18855 var currentMonth = this.date && this.date.getUTCMonth();
18857 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18859 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18860 v.removeClass('active');
18862 if(currentYear === year && k === currentMonth){
18863 v.addClass('active');
18866 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18867 v.addClass('disabled');
18873 year = parseInt(year/10, 10) * 10;
18875 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18877 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18880 for (var i = -1; i < 11; i++) {
18881 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18883 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18891 showMode: function(dir)
18894 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18897 Roo.each(this.picker().select('>div',true).elements, function(v){
18898 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18901 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18906 if(this.isInline) {
18910 this.picker().removeClass(['bottom', 'top']);
18912 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18914 * place to the top of element!
18918 this.picker().addClass('top');
18919 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18924 this.picker().addClass('bottom');
18926 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18929 parseDate : function(value)
18931 if(!value || value instanceof Date){
18934 var v = Date.parseDate(value, this.format);
18935 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18936 v = Date.parseDate(value, 'Y-m-d');
18938 if(!v && this.altFormats){
18939 if(!this.altFormatsArray){
18940 this.altFormatsArray = this.altFormats.split("|");
18942 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18943 v = Date.parseDate(value, this.altFormatsArray[i]);
18949 formatDate : function(date, fmt)
18951 return (!date || !(date instanceof Date)) ?
18952 date : date.dateFormat(fmt || this.format);
18955 onFocus : function()
18957 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18961 onBlur : function()
18963 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18965 var d = this.inputEl().getValue();
18972 showPopup : function()
18974 this.picker().show();
18978 this.fireEvent('showpopup', this, this.date);
18981 hidePopup : function()
18983 if(this.isInline) {
18986 this.picker().hide();
18987 this.viewMode = this.startViewMode;
18990 this.fireEvent('hidepopup', this, this.date);
18994 onMousedown: function(e)
18996 e.stopPropagation();
18997 e.preventDefault();
19002 Roo.bootstrap.DateField.superclass.keyup.call(this);
19006 setValue: function(v)
19008 if(this.fireEvent('beforeselect', this, v) !== false){
19009 var d = new Date(this.parseDate(v) ).clearTime();
19011 if(isNaN(d.getTime())){
19012 this.date = this.viewDate = '';
19013 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19017 v = this.formatDate(d);
19019 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19021 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19025 this.fireEvent('select', this, this.date);
19029 getValue: function()
19031 return this.formatDate(this.date);
19034 fireKey: function(e)
19036 if (!this.picker().isVisible()){
19037 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19043 var dateChanged = false,
19045 newDate, newViewDate;
19050 e.preventDefault();
19054 if (!this.keyboardNavigation) {
19057 dir = e.keyCode == 37 ? -1 : 1;
19060 newDate = this.moveYear(this.date, dir);
19061 newViewDate = this.moveYear(this.viewDate, dir);
19062 } else if (e.shiftKey){
19063 newDate = this.moveMonth(this.date, dir);
19064 newViewDate = this.moveMonth(this.viewDate, dir);
19066 newDate = new Date(this.date);
19067 newDate.setUTCDate(this.date.getUTCDate() + dir);
19068 newViewDate = new Date(this.viewDate);
19069 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19071 if (this.dateWithinRange(newDate)){
19072 this.date = newDate;
19073 this.viewDate = newViewDate;
19074 this.setValue(this.formatDate(this.date));
19076 e.preventDefault();
19077 dateChanged = true;
19082 if (!this.keyboardNavigation) {
19085 dir = e.keyCode == 38 ? -1 : 1;
19087 newDate = this.moveYear(this.date, dir);
19088 newViewDate = this.moveYear(this.viewDate, dir);
19089 } else if (e.shiftKey){
19090 newDate = this.moveMonth(this.date, dir);
19091 newViewDate = this.moveMonth(this.viewDate, dir);
19093 newDate = new Date(this.date);
19094 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19095 newViewDate = new Date(this.viewDate);
19096 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19098 if (this.dateWithinRange(newDate)){
19099 this.date = newDate;
19100 this.viewDate = newViewDate;
19101 this.setValue(this.formatDate(this.date));
19103 e.preventDefault();
19104 dateChanged = true;
19108 this.setValue(this.formatDate(this.date));
19110 e.preventDefault();
19113 this.setValue(this.formatDate(this.date));
19127 onClick: function(e)
19129 e.stopPropagation();
19130 e.preventDefault();
19132 var target = e.getTarget();
19134 if(target.nodeName.toLowerCase() === 'i'){
19135 target = Roo.get(target).dom.parentNode;
19138 var nodeName = target.nodeName;
19139 var className = target.className;
19140 var html = target.innerHTML;
19141 //Roo.log(nodeName);
19143 switch(nodeName.toLowerCase()) {
19145 switch(className) {
19151 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19152 switch(this.viewMode){
19154 this.viewDate = this.moveMonth(this.viewDate, dir);
19158 this.viewDate = this.moveYear(this.viewDate, dir);
19164 var date = new Date();
19165 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19167 this.setValue(this.formatDate(this.date));
19174 if (className.indexOf('disabled') < 0) {
19175 this.viewDate.setUTCDate(1);
19176 if (className.indexOf('month') > -1) {
19177 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19179 var year = parseInt(html, 10) || 0;
19180 this.viewDate.setUTCFullYear(year);
19184 if(this.singleMode){
19185 this.setValue(this.formatDate(this.viewDate));
19196 //Roo.log(className);
19197 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19198 var day = parseInt(html, 10) || 1;
19199 var year = this.viewDate.getUTCFullYear(),
19200 month = this.viewDate.getUTCMonth();
19202 if (className.indexOf('old') > -1) {
19209 } else if (className.indexOf('new') > -1) {
19217 //Roo.log([year,month,day]);
19218 this.date = this.UTCDate(year, month, day,0,0,0,0);
19219 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19221 //Roo.log(this.formatDate(this.date));
19222 this.setValue(this.formatDate(this.date));
19229 setStartDate: function(startDate)
19231 this.startDate = startDate || -Infinity;
19232 if (this.startDate !== -Infinity) {
19233 this.startDate = this.parseDate(this.startDate);
19236 this.updateNavArrows();
19239 setEndDate: function(endDate)
19241 this.endDate = endDate || Infinity;
19242 if (this.endDate !== Infinity) {
19243 this.endDate = this.parseDate(this.endDate);
19246 this.updateNavArrows();
19249 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19251 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19252 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19253 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19255 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19256 return parseInt(d, 10);
19259 this.updateNavArrows();
19262 updateNavArrows: function()
19264 if(this.singleMode){
19268 var d = new Date(this.viewDate),
19269 year = d.getUTCFullYear(),
19270 month = d.getUTCMonth();
19272 Roo.each(this.picker().select('.prev', true).elements, function(v){
19274 switch (this.viewMode) {
19277 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19283 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19290 Roo.each(this.picker().select('.next', true).elements, function(v){
19292 switch (this.viewMode) {
19295 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19301 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19309 moveMonth: function(date, dir)
19314 var new_date = new Date(date.valueOf()),
19315 day = new_date.getUTCDate(),
19316 month = new_date.getUTCMonth(),
19317 mag = Math.abs(dir),
19319 dir = dir > 0 ? 1 : -1;
19322 // If going back one month, make sure month is not current month
19323 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19325 return new_date.getUTCMonth() == month;
19327 // If going forward one month, make sure month is as expected
19328 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19330 return new_date.getUTCMonth() != new_month;
19332 new_month = month + dir;
19333 new_date.setUTCMonth(new_month);
19334 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19335 if (new_month < 0 || new_month > 11) {
19336 new_month = (new_month + 12) % 12;
19339 // For magnitudes >1, move one month at a time...
19340 for (var i=0; i<mag; i++) {
19341 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19342 new_date = this.moveMonth(new_date, dir);
19344 // ...then reset the day, keeping it in the new month
19345 new_month = new_date.getUTCMonth();
19346 new_date.setUTCDate(day);
19348 return new_month != new_date.getUTCMonth();
19351 // Common date-resetting loop -- if date is beyond end of month, make it
19354 new_date.setUTCDate(--day);
19355 new_date.setUTCMonth(new_month);
19360 moveYear: function(date, dir)
19362 return this.moveMonth(date, dir*12);
19365 dateWithinRange: function(date)
19367 return date >= this.startDate && date <= this.endDate;
19373 this.picker().remove();
19376 validateValue : function(value)
19378 if(this.getVisibilityEl().hasClass('hidden')){
19382 if(value.length < 1) {
19383 if(this.allowBlank){
19389 if(value.length < this.minLength){
19392 if(value.length > this.maxLength){
19396 var vt = Roo.form.VTypes;
19397 if(!vt[this.vtype](value, this)){
19401 if(typeof this.validator == "function"){
19402 var msg = this.validator(value);
19408 if(this.regex && !this.regex.test(value)){
19412 if(typeof(this.parseDate(value)) == 'undefined'){
19416 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19420 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19430 this.date = this.viewDate = '';
19432 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19437 Roo.apply(Roo.bootstrap.DateField, {
19448 html: '<i class="fa fa-arrow-left"/>'
19458 html: '<i class="fa fa-arrow-right"/>'
19500 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19501 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19502 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19503 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19504 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19517 navFnc: 'FullYear',
19522 navFnc: 'FullYear',
19527 Roo.apply(Roo.bootstrap.DateField, {
19531 cls: 'datepicker dropdown-menu roo-dynamic',
19535 cls: 'datepicker-days',
19539 cls: 'table-condensed',
19541 Roo.bootstrap.DateField.head,
19545 Roo.bootstrap.DateField.footer
19552 cls: 'datepicker-months',
19556 cls: 'table-condensed',
19558 Roo.bootstrap.DateField.head,
19559 Roo.bootstrap.DateField.content,
19560 Roo.bootstrap.DateField.footer
19567 cls: 'datepicker-years',
19571 cls: 'table-condensed',
19573 Roo.bootstrap.DateField.head,
19574 Roo.bootstrap.DateField.content,
19575 Roo.bootstrap.DateField.footer
19594 * @class Roo.bootstrap.TimeField
19595 * @extends Roo.bootstrap.Input
19596 * Bootstrap DateField class
19600 * Create a new TimeField
19601 * @param {Object} config The config object
19604 Roo.bootstrap.TimeField = function(config){
19605 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19609 * Fires when this field show.
19610 * @param {Roo.bootstrap.DateField} thisthis
19611 * @param {Mixed} date The date value
19616 * Fires when this field hide.
19617 * @param {Roo.bootstrap.DateField} this
19618 * @param {Mixed} date The date value
19623 * Fires when select a date.
19624 * @param {Roo.bootstrap.DateField} this
19625 * @param {Mixed} date The date value
19631 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19634 * @cfg {String} format
19635 * The default time format string which can be overriden for localization support. The format must be
19636 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19640 onRender: function(ct, position)
19643 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19645 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19647 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19649 this.pop = this.picker().select('>.datepicker-time',true).first();
19650 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19652 this.picker().on('mousedown', this.onMousedown, this);
19653 this.picker().on('click', this.onClick, this);
19655 this.picker().addClass('datepicker-dropdown');
19660 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19661 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19662 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19663 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19664 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19665 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19669 fireKey: function(e){
19670 if (!this.picker().isVisible()){
19671 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19677 e.preventDefault();
19685 this.onTogglePeriod();
19688 this.onIncrementMinutes();
19691 this.onDecrementMinutes();
19700 onClick: function(e) {
19701 e.stopPropagation();
19702 e.preventDefault();
19705 picker : function()
19707 return this.el.select('.datepicker', true).first();
19710 fillTime: function()
19712 var time = this.pop.select('tbody', true).first();
19714 time.dom.innerHTML = '';
19729 cls: 'hours-up glyphicon glyphicon-chevron-up'
19749 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19770 cls: 'timepicker-hour',
19785 cls: 'timepicker-minute',
19800 cls: 'btn btn-primary period',
19822 cls: 'hours-down glyphicon glyphicon-chevron-down'
19842 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19860 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19867 var hours = this.time.getHours();
19868 var minutes = this.time.getMinutes();
19881 hours = hours - 12;
19885 hours = '0' + hours;
19889 minutes = '0' + minutes;
19892 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19893 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19894 this.pop.select('button', true).first().dom.innerHTML = period;
19900 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19902 var cls = ['bottom'];
19904 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19911 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19916 this.picker().addClass(cls.join('-'));
19920 Roo.each(cls, function(c){
19922 _this.picker().setTop(_this.inputEl().getHeight());
19926 _this.picker().setTop(0 - _this.picker().getHeight());
19931 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19935 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19942 onFocus : function()
19944 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19948 onBlur : function()
19950 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19956 this.picker().show();
19961 this.fireEvent('show', this, this.date);
19966 this.picker().hide();
19969 this.fireEvent('hide', this, this.date);
19972 setTime : function()
19975 this.setValue(this.time.format(this.format));
19977 this.fireEvent('select', this, this.date);
19982 onMousedown: function(e){
19983 e.stopPropagation();
19984 e.preventDefault();
19987 onIncrementHours: function()
19989 Roo.log('onIncrementHours');
19990 this.time = this.time.add(Date.HOUR, 1);
19995 onDecrementHours: function()
19997 Roo.log('onDecrementHours');
19998 this.time = this.time.add(Date.HOUR, -1);
20002 onIncrementMinutes: function()
20004 Roo.log('onIncrementMinutes');
20005 this.time = this.time.add(Date.MINUTE, 1);
20009 onDecrementMinutes: function()
20011 Roo.log('onDecrementMinutes');
20012 this.time = this.time.add(Date.MINUTE, -1);
20016 onTogglePeriod: function()
20018 Roo.log('onTogglePeriod');
20019 this.time = this.time.add(Date.HOUR, 12);
20026 Roo.apply(Roo.bootstrap.TimeField, {
20056 cls: 'btn btn-info ok',
20068 Roo.apply(Roo.bootstrap.TimeField, {
20072 cls: 'datepicker dropdown-menu',
20076 cls: 'datepicker-time',
20080 cls: 'table-condensed',
20082 Roo.bootstrap.TimeField.content,
20083 Roo.bootstrap.TimeField.footer
20102 * @class Roo.bootstrap.MonthField
20103 * @extends Roo.bootstrap.Input
20104 * Bootstrap MonthField class
20106 * @cfg {String} language default en
20109 * Create a new MonthField
20110 * @param {Object} config The config object
20113 Roo.bootstrap.MonthField = function(config){
20114 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20119 * Fires when this field show.
20120 * @param {Roo.bootstrap.MonthField} this
20121 * @param {Mixed} date The date value
20126 * Fires when this field hide.
20127 * @param {Roo.bootstrap.MonthField} this
20128 * @param {Mixed} date The date value
20133 * Fires when select a date.
20134 * @param {Roo.bootstrap.MonthField} this
20135 * @param {String} oldvalue The old value
20136 * @param {String} newvalue The new value
20142 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20144 onRender: function(ct, position)
20147 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20149 this.language = this.language || 'en';
20150 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20151 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20153 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20154 this.isInline = false;
20155 this.isInput = true;
20156 this.component = this.el.select('.add-on', true).first() || false;
20157 this.component = (this.component && this.component.length === 0) ? false : this.component;
20158 this.hasInput = this.component && this.inputEL().length;
20160 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20162 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20164 this.picker().on('mousedown', this.onMousedown, this);
20165 this.picker().on('click', this.onClick, this);
20167 this.picker().addClass('datepicker-dropdown');
20169 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20170 v.setStyle('width', '189px');
20177 if(this.isInline) {
20183 setValue: function(v, suppressEvent)
20185 var o = this.getValue();
20187 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20191 if(suppressEvent !== true){
20192 this.fireEvent('select', this, o, v);
20197 getValue: function()
20202 onClick: function(e)
20204 e.stopPropagation();
20205 e.preventDefault();
20207 var target = e.getTarget();
20209 if(target.nodeName.toLowerCase() === 'i'){
20210 target = Roo.get(target).dom.parentNode;
20213 var nodeName = target.nodeName;
20214 var className = target.className;
20215 var html = target.innerHTML;
20217 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20221 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20223 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20229 picker : function()
20231 return this.pickerEl;
20234 fillMonths: function()
20237 var months = this.picker().select('>.datepicker-months td', true).first();
20239 months.dom.innerHTML = '';
20245 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20248 months.createChild(month);
20257 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20258 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20261 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20262 e.removeClass('active');
20264 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20265 e.addClass('active');
20272 if(this.isInline) {
20276 this.picker().removeClass(['bottom', 'top']);
20278 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20280 * place to the top of element!
20284 this.picker().addClass('top');
20285 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20290 this.picker().addClass('bottom');
20292 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20295 onFocus : function()
20297 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20301 onBlur : function()
20303 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20305 var d = this.inputEl().getValue();
20314 this.picker().show();
20315 this.picker().select('>.datepicker-months', true).first().show();
20319 this.fireEvent('show', this, this.date);
20324 if(this.isInline) {
20327 this.picker().hide();
20328 this.fireEvent('hide', this, this.date);
20332 onMousedown: function(e)
20334 e.stopPropagation();
20335 e.preventDefault();
20340 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20344 fireKey: function(e)
20346 if (!this.picker().isVisible()){
20347 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20358 e.preventDefault();
20362 dir = e.keyCode == 37 ? -1 : 1;
20364 this.vIndex = this.vIndex + dir;
20366 if(this.vIndex < 0){
20370 if(this.vIndex > 11){
20374 if(isNaN(this.vIndex)){
20378 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20384 dir = e.keyCode == 38 ? -1 : 1;
20386 this.vIndex = this.vIndex + dir * 4;
20388 if(this.vIndex < 0){
20392 if(this.vIndex > 11){
20396 if(isNaN(this.vIndex)){
20400 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20405 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20406 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20410 e.preventDefault();
20413 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20414 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20430 this.picker().remove();
20435 Roo.apply(Roo.bootstrap.MonthField, {
20454 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20455 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20460 Roo.apply(Roo.bootstrap.MonthField, {
20464 cls: 'datepicker dropdown-menu roo-dynamic',
20468 cls: 'datepicker-months',
20472 cls: 'table-condensed',
20474 Roo.bootstrap.DateField.content
20494 * @class Roo.bootstrap.CheckBox
20495 * @extends Roo.bootstrap.Input
20496 * Bootstrap CheckBox class
20498 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20499 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20500 * @cfg {String} boxLabel The text that appears beside the checkbox
20501 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20502 * @cfg {Boolean} checked initnal the element
20503 * @cfg {Boolean} inline inline the element (default false)
20504 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20505 * @cfg {String} tooltip label tooltip
20508 * Create a new CheckBox
20509 * @param {Object} config The config object
20512 Roo.bootstrap.CheckBox = function(config){
20513 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20518 * Fires when the element is checked or unchecked.
20519 * @param {Roo.bootstrap.CheckBox} this This input
20520 * @param {Boolean} checked The new checked value
20525 * Fires when the element is click.
20526 * @param {Roo.bootstrap.CheckBox} this This input
20533 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20535 inputType: 'checkbox',
20544 getAutoCreate : function()
20546 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20552 cfg.cls = 'form-group ' + this.inputType; //input-group
20555 cfg.cls += ' ' + this.inputType + '-inline';
20561 type : this.inputType,
20562 value : this.inputValue,
20563 cls : 'roo-' + this.inputType, //'form-box',
20564 placeholder : this.placeholder || ''
20568 if(this.inputType != 'radio'){
20572 cls : 'roo-hidden-value',
20573 value : this.checked ? this.inputValue : this.valueOff
20578 if (this.weight) { // Validity check?
20579 cfg.cls += " " + this.inputType + "-" + this.weight;
20582 if (this.disabled) {
20583 input.disabled=true;
20587 input.checked = this.checked;
20592 input.name = this.name;
20594 if(this.inputType != 'radio'){
20595 hidden.name = this.name;
20596 input.name = '_hidden_' + this.name;
20601 input.cls += ' input-' + this.size;
20606 ['xs','sm','md','lg'].map(function(size){
20607 if (settings[size]) {
20608 cfg.cls += ' col-' + size + '-' + settings[size];
20612 var inputblock = input;
20614 if (this.before || this.after) {
20617 cls : 'input-group',
20622 inputblock.cn.push({
20624 cls : 'input-group-addon',
20629 inputblock.cn.push(input);
20631 if(this.inputType != 'radio'){
20632 inputblock.cn.push(hidden);
20636 inputblock.cn.push({
20638 cls : 'input-group-addon',
20645 if (align ==='left' && this.fieldLabel.length) {
20646 // Roo.log("left and has label");
20651 cls : 'control-label',
20652 html : this.fieldLabel
20662 if(this.labelWidth > 12){
20663 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20666 if(this.labelWidth < 13 && this.labelmd == 0){
20667 this.labelmd = this.labelWidth;
20670 if(this.labellg > 0){
20671 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20672 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20675 if(this.labelmd > 0){
20676 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20677 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20680 if(this.labelsm > 0){
20681 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20682 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20685 if(this.labelxs > 0){
20686 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20687 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20690 } else if ( this.fieldLabel.length) {
20691 // Roo.log(" label");
20695 tag: this.boxLabel ? 'span' : 'label',
20697 cls: 'control-label box-input-label',
20698 //cls : 'input-group-addon',
20699 html : this.fieldLabel
20708 // Roo.log(" no label && no align");
20709 cfg.cn = [ inputblock ] ;
20715 var boxLabelCfg = {
20717 //'for': id, // box label is handled by onclick - so no for...
20719 html: this.boxLabel
20723 boxLabelCfg.tooltip = this.tooltip;
20726 cfg.cn.push(boxLabelCfg);
20729 if(this.inputType != 'radio'){
20730 cfg.cn.push(hidden);
20738 * return the real input element.
20740 inputEl: function ()
20742 return this.el.select('input.roo-' + this.inputType,true).first();
20744 hiddenEl: function ()
20746 return this.el.select('input.roo-hidden-value',true).first();
20749 labelEl: function()
20751 return this.el.select('label.control-label',true).first();
20753 /* depricated... */
20757 return this.labelEl();
20760 boxLabelEl: function()
20762 return this.el.select('label.box-label',true).first();
20765 initEvents : function()
20767 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20769 this.inputEl().on('click', this.onClick, this);
20771 if (this.boxLabel) {
20772 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20775 this.startValue = this.getValue();
20778 Roo.bootstrap.CheckBox.register(this);
20782 onClick : function(e)
20784 if(this.fireEvent('click', this, e) !== false){
20785 this.setChecked(!this.checked);
20790 setChecked : function(state,suppressEvent)
20792 this.startValue = this.getValue();
20794 if(this.inputType == 'radio'){
20796 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20797 e.dom.checked = false;
20800 this.inputEl().dom.checked = true;
20802 this.inputEl().dom.value = this.inputValue;
20804 if(suppressEvent !== true){
20805 this.fireEvent('check', this, true);
20813 this.checked = state;
20815 this.inputEl().dom.checked = state;
20818 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20820 if(suppressEvent !== true){
20821 this.fireEvent('check', this, state);
20827 getValue : function()
20829 if(this.inputType == 'radio'){
20830 return this.getGroupValue();
20833 return this.hiddenEl().dom.value;
20837 getGroupValue : function()
20839 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20843 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20846 setValue : function(v,suppressEvent)
20848 if(this.inputType == 'radio'){
20849 this.setGroupValue(v, suppressEvent);
20853 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20858 setGroupValue : function(v, suppressEvent)
20860 this.startValue = this.getValue();
20862 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20863 e.dom.checked = false;
20865 if(e.dom.value == v){
20866 e.dom.checked = true;
20870 if(suppressEvent !== true){
20871 this.fireEvent('check', this, true);
20879 validate : function()
20881 if(this.getVisibilityEl().hasClass('hidden')){
20887 (this.inputType == 'radio' && this.validateRadio()) ||
20888 (this.inputType == 'checkbox' && this.validateCheckbox())
20894 this.markInvalid();
20898 validateRadio : function()
20900 if(this.getVisibilityEl().hasClass('hidden')){
20904 if(this.allowBlank){
20910 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20911 if(!e.dom.checked){
20923 validateCheckbox : function()
20926 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20927 //return (this.getValue() == this.inputValue) ? true : false;
20930 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20938 for(var i in group){
20939 if(group[i].el.isVisible(true)){
20947 for(var i in group){
20952 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20959 * Mark this field as valid
20961 markValid : function()
20965 this.fireEvent('valid', this);
20967 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20970 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20977 if(this.inputType == 'radio'){
20978 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20979 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20980 e.findParent('.form-group', false, true).addClass(_this.validClass);
20987 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20988 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20992 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20998 for(var i in group){
20999 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21000 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21005 * Mark this field as invalid
21006 * @param {String} msg The validation message
21008 markInvalid : function(msg)
21010 if(this.allowBlank){
21016 this.fireEvent('invalid', this, msg);
21018 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21021 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21025 label.markInvalid();
21028 if(this.inputType == 'radio'){
21029 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21030 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21031 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21038 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21039 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21043 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21049 for(var i in group){
21050 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21051 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21056 clearInvalid : function()
21058 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21060 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21062 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21064 if (label && label.iconEl) {
21065 label.iconEl.removeClass(label.validClass);
21066 label.iconEl.removeClass(label.invalidClass);
21070 disable : function()
21072 if(this.inputType != 'radio'){
21073 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21080 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21081 _this.getActionEl().addClass(this.disabledClass);
21082 e.dom.disabled = true;
21086 this.disabled = true;
21087 this.fireEvent("disable", this);
21091 enable : function()
21093 if(this.inputType != 'radio'){
21094 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21101 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21102 _this.getActionEl().removeClass(this.disabledClass);
21103 e.dom.disabled = false;
21107 this.disabled = false;
21108 this.fireEvent("enable", this);
21112 setBoxLabel : function(v)
21117 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21123 Roo.apply(Roo.bootstrap.CheckBox, {
21128 * register a CheckBox Group
21129 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21131 register : function(checkbox)
21133 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21134 this.groups[checkbox.groupId] = {};
21137 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21141 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21145 * fetch a CheckBox Group based on the group ID
21146 * @param {string} the group ID
21147 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21149 get: function(groupId) {
21150 if (typeof(this.groups[groupId]) == 'undefined') {
21154 return this.groups[groupId] ;
21167 * @class Roo.bootstrap.Radio
21168 * @extends Roo.bootstrap.Component
21169 * Bootstrap Radio class
21170 * @cfg {String} boxLabel - the label associated
21171 * @cfg {String} value - the value of radio
21174 * Create a new Radio
21175 * @param {Object} config The config object
21177 Roo.bootstrap.Radio = function(config){
21178 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21182 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21188 getAutoCreate : function()
21192 cls : 'form-group radio',
21197 html : this.boxLabel
21205 initEvents : function()
21207 this.parent().register(this);
21209 this.el.on('click', this.onClick, this);
21213 onClick : function(e)
21215 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21216 this.setChecked(true);
21220 setChecked : function(state, suppressEvent)
21222 this.parent().setValue(this.value, suppressEvent);
21226 setBoxLabel : function(v)
21231 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21246 * @class Roo.bootstrap.SecurePass
21247 * @extends Roo.bootstrap.Input
21248 * Bootstrap SecurePass class
21252 * Create a new SecurePass
21253 * @param {Object} config The config object
21256 Roo.bootstrap.SecurePass = function (config) {
21257 // these go here, so the translation tool can replace them..
21259 PwdEmpty: "Please type a password, and then retype it to confirm.",
21260 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21261 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21262 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21263 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21264 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21265 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21266 TooWeak: "Your password is Too Weak."
21268 this.meterLabel = "Password strength:";
21269 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21270 this.meterClass = [
21271 "roo-password-meter-tooweak",
21272 "roo-password-meter-weak",
21273 "roo-password-meter-medium",
21274 "roo-password-meter-strong",
21275 "roo-password-meter-grey"
21280 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21283 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21285 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21287 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21288 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21289 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21290 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21291 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21292 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21293 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21303 * @cfg {String/Object} Label for the strength meter (defaults to
21304 * 'Password strength:')
21309 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21310 * ['Weak', 'Medium', 'Strong'])
21313 pwdStrengths: false,
21326 initEvents: function ()
21328 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21330 if (this.el.is('input[type=password]') && Roo.isSafari) {
21331 this.el.on('keydown', this.SafariOnKeyDown, this);
21334 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21337 onRender: function (ct, position)
21339 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21340 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21341 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21343 this.trigger.createChild({
21348 cls: 'roo-password-meter-grey col-xs-12',
21351 //width: this.meterWidth + 'px'
21355 cls: 'roo-password-meter-text'
21361 if (this.hideTrigger) {
21362 this.trigger.setDisplayed(false);
21364 this.setSize(this.width || '', this.height || '');
21367 onDestroy: function ()
21369 if (this.trigger) {
21370 this.trigger.removeAllListeners();
21371 this.trigger.remove();
21374 this.wrap.remove();
21376 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21379 checkStrength: function ()
21381 var pwd = this.inputEl().getValue();
21382 if (pwd == this._lastPwd) {
21387 if (this.ClientSideStrongPassword(pwd)) {
21389 } else if (this.ClientSideMediumPassword(pwd)) {
21391 } else if (this.ClientSideWeakPassword(pwd)) {
21397 Roo.log('strength1: ' + strength);
21399 //var pm = this.trigger.child('div/div/div').dom;
21400 var pm = this.trigger.child('div/div');
21401 pm.removeClass(this.meterClass);
21402 pm.addClass(this.meterClass[strength]);
21405 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21407 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21409 this._lastPwd = pwd;
21413 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21415 this._lastPwd = '';
21417 var pm = this.trigger.child('div/div');
21418 pm.removeClass(this.meterClass);
21419 pm.addClass('roo-password-meter-grey');
21422 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21425 this.inputEl().dom.type='password';
21428 validateValue: function (value)
21431 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21434 if (value.length == 0) {
21435 if (this.allowBlank) {
21436 this.clearInvalid();
21440 this.markInvalid(this.errors.PwdEmpty);
21441 this.errorMsg = this.errors.PwdEmpty;
21449 if ('[\x21-\x7e]*'.match(value)) {
21450 this.markInvalid(this.errors.PwdBadChar);
21451 this.errorMsg = this.errors.PwdBadChar;
21454 if (value.length < 6) {
21455 this.markInvalid(this.errors.PwdShort);
21456 this.errorMsg = this.errors.PwdShort;
21459 if (value.length > 16) {
21460 this.markInvalid(this.errors.PwdLong);
21461 this.errorMsg = this.errors.PwdLong;
21465 if (this.ClientSideStrongPassword(value)) {
21467 } else if (this.ClientSideMediumPassword(value)) {
21469 } else if (this.ClientSideWeakPassword(value)) {
21476 if (strength < 2) {
21477 //this.markInvalid(this.errors.TooWeak);
21478 this.errorMsg = this.errors.TooWeak;
21483 console.log('strength2: ' + strength);
21485 //var pm = this.trigger.child('div/div/div').dom;
21487 var pm = this.trigger.child('div/div');
21488 pm.removeClass(this.meterClass);
21489 pm.addClass(this.meterClass[strength]);
21491 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21493 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21495 this.errorMsg = '';
21499 CharacterSetChecks: function (type)
21502 this.fResult = false;
21505 isctype: function (character, type)
21508 case this.kCapitalLetter:
21509 if (character >= 'A' && character <= 'Z') {
21514 case this.kSmallLetter:
21515 if (character >= 'a' && character <= 'z') {
21521 if (character >= '0' && character <= '9') {
21526 case this.kPunctuation:
21527 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21538 IsLongEnough: function (pwd, size)
21540 return !(pwd == null || isNaN(size) || pwd.length < size);
21543 SpansEnoughCharacterSets: function (word, nb)
21545 if (!this.IsLongEnough(word, nb))
21550 var characterSetChecks = new Array(
21551 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21552 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21555 for (var index = 0; index < word.length; ++index) {
21556 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21557 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21558 characterSetChecks[nCharSet].fResult = true;
21565 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21566 if (characterSetChecks[nCharSet].fResult) {
21571 if (nCharSets < nb) {
21577 ClientSideStrongPassword: function (pwd)
21579 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21582 ClientSideMediumPassword: function (pwd)
21584 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21587 ClientSideWeakPassword: function (pwd)
21589 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21592 })//<script type="text/javascript">
21595 * Based Ext JS Library 1.1.1
21596 * Copyright(c) 2006-2007, Ext JS, LLC.
21602 * @class Roo.HtmlEditorCore
21603 * @extends Roo.Component
21604 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21606 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21609 Roo.HtmlEditorCore = function(config){
21612 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21617 * @event initialize
21618 * Fires when the editor is fully initialized (including the iframe)
21619 * @param {Roo.HtmlEditorCore} this
21624 * Fires when the editor is first receives the focus. Any insertion must wait
21625 * until after this event.
21626 * @param {Roo.HtmlEditorCore} this
21630 * @event beforesync
21631 * Fires before the textarea is updated with content from the editor iframe. Return false
21632 * to cancel the sync.
21633 * @param {Roo.HtmlEditorCore} this
21634 * @param {String} html
21638 * @event beforepush
21639 * Fires before the iframe editor is updated with content from the textarea. Return false
21640 * to cancel the push.
21641 * @param {Roo.HtmlEditorCore} this
21642 * @param {String} html
21647 * Fires when the textarea is updated with content from the editor iframe.
21648 * @param {Roo.HtmlEditorCore} this
21649 * @param {String} html
21654 * Fires when the iframe editor is updated with content from the textarea.
21655 * @param {Roo.HtmlEditorCore} this
21656 * @param {String} html
21661 * @event editorevent
21662 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21663 * @param {Roo.HtmlEditorCore} this
21669 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21671 // defaults : white / black...
21672 this.applyBlacklists();
21679 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21683 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21689 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21694 * @cfg {Number} height (in pixels)
21698 * @cfg {Number} width (in pixels)
21703 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21706 stylesheets: false,
21711 // private properties
21712 validationEvent : false,
21714 initialized : false,
21716 sourceEditMode : false,
21717 onFocus : Roo.emptyFn,
21719 hideMode:'offsets',
21723 // blacklist + whitelisted elements..
21730 * Protected method that will not generally be called directly. It
21731 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21732 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21734 getDocMarkup : function(){
21738 // inherit styels from page...??
21739 if (this.stylesheets === false) {
21741 Roo.get(document.head).select('style').each(function(node) {
21742 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21745 Roo.get(document.head).select('link').each(function(node) {
21746 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21749 } else if (!this.stylesheets.length) {
21751 st = '<style type="text/css">' +
21752 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21755 st = '<style type="text/css">' +
21760 st += '<style type="text/css">' +
21761 'IMG { cursor: pointer } ' +
21764 var cls = 'roo-htmleditor-body';
21766 if(this.bodyCls.length){
21767 cls += ' ' + this.bodyCls;
21770 return '<html><head>' + st +
21771 //<style type="text/css">' +
21772 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21774 ' </head><body class="' + cls + '"></body></html>';
21778 onRender : function(ct, position)
21781 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21782 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21785 this.el.dom.style.border = '0 none';
21786 this.el.dom.setAttribute('tabIndex', -1);
21787 this.el.addClass('x-hidden hide');
21791 if(Roo.isIE){ // fix IE 1px bogus margin
21792 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21796 this.frameId = Roo.id();
21800 var iframe = this.owner.wrap.createChild({
21802 cls: 'form-control', // bootstrap..
21804 name: this.frameId,
21805 frameBorder : 'no',
21806 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21811 this.iframe = iframe.dom;
21813 this.assignDocWin();
21815 this.doc.designMode = 'on';
21818 this.doc.write(this.getDocMarkup());
21822 var task = { // must defer to wait for browser to be ready
21824 //console.log("run task?" + this.doc.readyState);
21825 this.assignDocWin();
21826 if(this.doc.body || this.doc.readyState == 'complete'){
21828 this.doc.designMode="on";
21832 Roo.TaskMgr.stop(task);
21833 this.initEditor.defer(10, this);
21840 Roo.TaskMgr.start(task);
21845 onResize : function(w, h)
21847 Roo.log('resize: ' +w + ',' + h );
21848 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21852 if(typeof w == 'number'){
21854 this.iframe.style.width = w + 'px';
21856 if(typeof h == 'number'){
21858 this.iframe.style.height = h + 'px';
21860 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21867 * Toggles the editor between standard and source edit mode.
21868 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21870 toggleSourceEdit : function(sourceEditMode){
21872 this.sourceEditMode = sourceEditMode === true;
21874 if(this.sourceEditMode){
21876 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21879 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21880 //this.iframe.className = '';
21883 //this.setSize(this.owner.wrap.getSize());
21884 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21891 * Protected method that will not generally be called directly. If you need/want
21892 * custom HTML cleanup, this is the method you should override.
21893 * @param {String} html The HTML to be cleaned
21894 * return {String} The cleaned HTML
21896 cleanHtml : function(html){
21897 html = String(html);
21898 if(html.length > 5){
21899 if(Roo.isSafari){ // strip safari nonsense
21900 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21903 if(html == ' '){
21910 * HTML Editor -> Textarea
21911 * Protected method that will not generally be called directly. Syncs the contents
21912 * of the editor iframe with the textarea.
21914 syncValue : function(){
21915 if(this.initialized){
21916 var bd = (this.doc.body || this.doc.documentElement);
21917 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21918 var html = bd.innerHTML;
21920 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21921 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21923 html = '<div style="'+m[0]+'">' + html + '</div>';
21926 html = this.cleanHtml(html);
21927 // fix up the special chars.. normaly like back quotes in word...
21928 // however we do not want to do this with chinese..
21929 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21930 var cc = b.charCodeAt();
21932 (cc >= 0x4E00 && cc < 0xA000 ) ||
21933 (cc >= 0x3400 && cc < 0x4E00 ) ||
21934 (cc >= 0xf900 && cc < 0xfb00 )
21940 if(this.owner.fireEvent('beforesync', this, html) !== false){
21941 this.el.dom.value = html;
21942 this.owner.fireEvent('sync', this, html);
21948 * Protected method that will not generally be called directly. Pushes the value of the textarea
21949 * into the iframe editor.
21951 pushValue : function(){
21952 if(this.initialized){
21953 var v = this.el.dom.value.trim();
21955 // if(v.length < 1){
21959 if(this.owner.fireEvent('beforepush', this, v) !== false){
21960 var d = (this.doc.body || this.doc.documentElement);
21962 this.cleanUpPaste();
21963 this.el.dom.value = d.innerHTML;
21964 this.owner.fireEvent('push', this, v);
21970 deferFocus : function(){
21971 this.focus.defer(10, this);
21975 focus : function(){
21976 if(this.win && !this.sourceEditMode){
21983 assignDocWin: function()
21985 var iframe = this.iframe;
21988 this.doc = iframe.contentWindow.document;
21989 this.win = iframe.contentWindow;
21991 // if (!Roo.get(this.frameId)) {
21994 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21995 // this.win = Roo.get(this.frameId).dom.contentWindow;
21997 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22001 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22002 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22007 initEditor : function(){
22008 //console.log("INIT EDITOR");
22009 this.assignDocWin();
22013 this.doc.designMode="on";
22015 this.doc.write(this.getDocMarkup());
22018 var dbody = (this.doc.body || this.doc.documentElement);
22019 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22020 // this copies styles from the containing element into thsi one..
22021 // not sure why we need all of this..
22022 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22024 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22025 //ss['background-attachment'] = 'fixed'; // w3c
22026 dbody.bgProperties = 'fixed'; // ie
22027 //Roo.DomHelper.applyStyles(dbody, ss);
22028 Roo.EventManager.on(this.doc, {
22029 //'mousedown': this.onEditorEvent,
22030 'mouseup': this.onEditorEvent,
22031 'dblclick': this.onEditorEvent,
22032 'click': this.onEditorEvent,
22033 'keyup': this.onEditorEvent,
22038 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22040 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22041 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22043 this.initialized = true;
22045 this.owner.fireEvent('initialize', this);
22050 onDestroy : function(){
22056 //for (var i =0; i < this.toolbars.length;i++) {
22057 // // fixme - ask toolbars for heights?
22058 // this.toolbars[i].onDestroy();
22061 //this.wrap.dom.innerHTML = '';
22062 //this.wrap.remove();
22067 onFirstFocus : function(){
22069 this.assignDocWin();
22072 this.activated = true;
22075 if(Roo.isGecko){ // prevent silly gecko errors
22077 var s = this.win.getSelection();
22078 if(!s.focusNode || s.focusNode.nodeType != 3){
22079 var r = s.getRangeAt(0);
22080 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22085 this.execCmd('useCSS', true);
22086 this.execCmd('styleWithCSS', false);
22089 this.owner.fireEvent('activate', this);
22093 adjustFont: function(btn){
22094 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22095 //if(Roo.isSafari){ // safari
22098 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22099 if(Roo.isSafari){ // safari
22100 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22101 v = (v < 10) ? 10 : v;
22102 v = (v > 48) ? 48 : v;
22103 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22108 v = Math.max(1, v+adjust);
22110 this.execCmd('FontSize', v );
22113 onEditorEvent : function(e)
22115 this.owner.fireEvent('editorevent', this, e);
22116 // this.updateToolbar();
22117 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22120 insertTag : function(tg)
22122 // could be a bit smarter... -> wrap the current selected tRoo..
22123 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22125 range = this.createRange(this.getSelection());
22126 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22127 wrappingNode.appendChild(range.extractContents());
22128 range.insertNode(wrappingNode);
22135 this.execCmd("formatblock", tg);
22139 insertText : function(txt)
22143 var range = this.createRange();
22144 range.deleteContents();
22145 //alert(Sender.getAttribute('label'));
22147 range.insertNode(this.doc.createTextNode(txt));
22153 * Executes a Midas editor command on the editor document and performs necessary focus and
22154 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22155 * @param {String} cmd The Midas command
22156 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22158 relayCmd : function(cmd, value){
22160 this.execCmd(cmd, value);
22161 this.owner.fireEvent('editorevent', this);
22162 //this.updateToolbar();
22163 this.owner.deferFocus();
22167 * Executes a Midas editor command directly on the editor document.
22168 * For visual commands, you should use {@link #relayCmd} instead.
22169 * <b>This should only be called after the editor is initialized.</b>
22170 * @param {String} cmd The Midas command
22171 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22173 execCmd : function(cmd, value){
22174 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22181 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22183 * @param {String} text | dom node..
22185 insertAtCursor : function(text)
22188 if(!this.activated){
22194 var r = this.doc.selection.createRange();
22205 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22209 // from jquery ui (MIT licenced)
22211 var win = this.win;
22213 if (win.getSelection && win.getSelection().getRangeAt) {
22214 range = win.getSelection().getRangeAt(0);
22215 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22216 range.insertNode(node);
22217 } else if (win.document.selection && win.document.selection.createRange) {
22218 // no firefox support
22219 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22220 win.document.selection.createRange().pasteHTML(txt);
22222 // no firefox support
22223 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22224 this.execCmd('InsertHTML', txt);
22233 mozKeyPress : function(e){
22235 var c = e.getCharCode(), cmd;
22238 c = String.fromCharCode(c).toLowerCase();
22252 this.cleanUpPaste.defer(100, this);
22260 e.preventDefault();
22268 fixKeys : function(){ // load time branching for fastest keydown performance
22270 return function(e){
22271 var k = e.getKey(), r;
22274 r = this.doc.selection.createRange();
22277 r.pasteHTML('    ');
22284 r = this.doc.selection.createRange();
22286 var target = r.parentElement();
22287 if(!target || target.tagName.toLowerCase() != 'li'){
22289 r.pasteHTML('<br />');
22295 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22296 this.cleanUpPaste.defer(100, this);
22302 }else if(Roo.isOpera){
22303 return function(e){
22304 var k = e.getKey();
22308 this.execCmd('InsertHTML','    ');
22311 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22312 this.cleanUpPaste.defer(100, this);
22317 }else if(Roo.isSafari){
22318 return function(e){
22319 var k = e.getKey();
22323 this.execCmd('InsertText','\t');
22327 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22328 this.cleanUpPaste.defer(100, this);
22336 getAllAncestors: function()
22338 var p = this.getSelectedNode();
22341 a.push(p); // push blank onto stack..
22342 p = this.getParentElement();
22346 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22350 a.push(this.doc.body);
22354 lastSelNode : false,
22357 getSelection : function()
22359 this.assignDocWin();
22360 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22363 getSelectedNode: function()
22365 // this may only work on Gecko!!!
22367 // should we cache this!!!!
22372 var range = this.createRange(this.getSelection()).cloneRange();
22375 var parent = range.parentElement();
22377 var testRange = range.duplicate();
22378 testRange.moveToElementText(parent);
22379 if (testRange.inRange(range)) {
22382 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22385 parent = parent.parentElement;
22390 // is ancestor a text element.
22391 var ac = range.commonAncestorContainer;
22392 if (ac.nodeType == 3) {
22393 ac = ac.parentNode;
22396 var ar = ac.childNodes;
22399 var other_nodes = [];
22400 var has_other_nodes = false;
22401 for (var i=0;i<ar.length;i++) {
22402 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22405 // fullly contained node.
22407 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22412 // probably selected..
22413 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22414 other_nodes.push(ar[i]);
22418 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22423 has_other_nodes = true;
22425 if (!nodes.length && other_nodes.length) {
22426 nodes= other_nodes;
22428 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22434 createRange: function(sel)
22436 // this has strange effects when using with
22437 // top toolbar - not sure if it's a great idea.
22438 //this.editor.contentWindow.focus();
22439 if (typeof sel != "undefined") {
22441 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22443 return this.doc.createRange();
22446 return this.doc.createRange();
22449 getParentElement: function()
22452 this.assignDocWin();
22453 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22455 var range = this.createRange(sel);
22458 var p = range.commonAncestorContainer;
22459 while (p.nodeType == 3) { // text node
22470 * Range intersection.. the hard stuff...
22474 * [ -- selected range --- ]
22478 * if end is before start or hits it. fail.
22479 * if start is after end or hits it fail.
22481 * if either hits (but other is outside. - then it's not
22487 // @see http://www.thismuchiknow.co.uk/?p=64.
22488 rangeIntersectsNode : function(range, node)
22490 var nodeRange = node.ownerDocument.createRange();
22492 nodeRange.selectNode(node);
22494 nodeRange.selectNodeContents(node);
22497 var rangeStartRange = range.cloneRange();
22498 rangeStartRange.collapse(true);
22500 var rangeEndRange = range.cloneRange();
22501 rangeEndRange.collapse(false);
22503 var nodeStartRange = nodeRange.cloneRange();
22504 nodeStartRange.collapse(true);
22506 var nodeEndRange = nodeRange.cloneRange();
22507 nodeEndRange.collapse(false);
22509 return rangeStartRange.compareBoundaryPoints(
22510 Range.START_TO_START, nodeEndRange) == -1 &&
22511 rangeEndRange.compareBoundaryPoints(
22512 Range.START_TO_START, nodeStartRange) == 1;
22516 rangeCompareNode : function(range, node)
22518 var nodeRange = node.ownerDocument.createRange();
22520 nodeRange.selectNode(node);
22522 nodeRange.selectNodeContents(node);
22526 range.collapse(true);
22528 nodeRange.collapse(true);
22530 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22531 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22533 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22535 var nodeIsBefore = ss == 1;
22536 var nodeIsAfter = ee == -1;
22538 if (nodeIsBefore && nodeIsAfter) {
22541 if (!nodeIsBefore && nodeIsAfter) {
22542 return 1; //right trailed.
22545 if (nodeIsBefore && !nodeIsAfter) {
22546 return 2; // left trailed.
22552 // private? - in a new class?
22553 cleanUpPaste : function()
22555 // cleans up the whole document..
22556 Roo.log('cleanuppaste');
22558 this.cleanUpChildren(this.doc.body);
22559 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22560 if (clean != this.doc.body.innerHTML) {
22561 this.doc.body.innerHTML = clean;
22566 cleanWordChars : function(input) {// change the chars to hex code
22567 var he = Roo.HtmlEditorCore;
22569 var output = input;
22570 Roo.each(he.swapCodes, function(sw) {
22571 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22573 output = output.replace(swapper, sw[1]);
22580 cleanUpChildren : function (n)
22582 if (!n.childNodes.length) {
22585 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22586 this.cleanUpChild(n.childNodes[i]);
22593 cleanUpChild : function (node)
22596 //console.log(node);
22597 if (node.nodeName == "#text") {
22598 // clean up silly Windows -- stuff?
22601 if (node.nodeName == "#comment") {
22602 node.parentNode.removeChild(node);
22603 // clean up silly Windows -- stuff?
22606 var lcname = node.tagName.toLowerCase();
22607 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22608 // whitelist of tags..
22610 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22612 node.parentNode.removeChild(node);
22617 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22619 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22620 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22622 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22623 // remove_keep_children = true;
22626 if (remove_keep_children) {
22627 this.cleanUpChildren(node);
22628 // inserts everything just before this node...
22629 while (node.childNodes.length) {
22630 var cn = node.childNodes[0];
22631 node.removeChild(cn);
22632 node.parentNode.insertBefore(cn, node);
22634 node.parentNode.removeChild(node);
22638 if (!node.attributes || !node.attributes.length) {
22639 this.cleanUpChildren(node);
22643 function cleanAttr(n,v)
22646 if (v.match(/^\./) || v.match(/^\//)) {
22649 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22652 if (v.match(/^#/)) {
22655 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22656 node.removeAttribute(n);
22660 var cwhite = this.cwhite;
22661 var cblack = this.cblack;
22663 function cleanStyle(n,v)
22665 if (v.match(/expression/)) { //XSS?? should we even bother..
22666 node.removeAttribute(n);
22670 var parts = v.split(/;/);
22673 Roo.each(parts, function(p) {
22674 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22678 var l = p.split(':').shift().replace(/\s+/g,'');
22679 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22681 if ( cwhite.length && cblack.indexOf(l) > -1) {
22682 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22683 //node.removeAttribute(n);
22687 // only allow 'c whitelisted system attributes'
22688 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22689 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22690 //node.removeAttribute(n);
22700 if (clean.length) {
22701 node.setAttribute(n, clean.join(';'));
22703 node.removeAttribute(n);
22709 for (var i = node.attributes.length-1; i > -1 ; i--) {
22710 var a = node.attributes[i];
22713 if (a.name.toLowerCase().substr(0,2)=='on') {
22714 node.removeAttribute(a.name);
22717 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22718 node.removeAttribute(a.name);
22721 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22722 cleanAttr(a.name,a.value); // fixme..
22725 if (a.name == 'style') {
22726 cleanStyle(a.name,a.value);
22729 /// clean up MS crap..
22730 // tecnically this should be a list of valid class'es..
22733 if (a.name == 'class') {
22734 if (a.value.match(/^Mso/)) {
22735 node.className = '';
22738 if (a.value.match(/^body$/)) {
22739 node.className = '';
22750 this.cleanUpChildren(node);
22756 * Clean up MS wordisms...
22758 cleanWord : function(node)
22763 this.cleanWord(this.doc.body);
22766 if (node.nodeName == "#text") {
22767 // clean up silly Windows -- stuff?
22770 if (node.nodeName == "#comment") {
22771 node.parentNode.removeChild(node);
22772 // clean up silly Windows -- stuff?
22776 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22777 node.parentNode.removeChild(node);
22781 // remove - but keep children..
22782 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22783 while (node.childNodes.length) {
22784 var cn = node.childNodes[0];
22785 node.removeChild(cn);
22786 node.parentNode.insertBefore(cn, node);
22788 node.parentNode.removeChild(node);
22789 this.iterateChildren(node, this.cleanWord);
22793 if (node.className.length) {
22795 var cn = node.className.split(/\W+/);
22797 Roo.each(cn, function(cls) {
22798 if (cls.match(/Mso[a-zA-Z]+/)) {
22803 node.className = cna.length ? cna.join(' ') : '';
22805 node.removeAttribute("class");
22809 if (node.hasAttribute("lang")) {
22810 node.removeAttribute("lang");
22813 if (node.hasAttribute("style")) {
22815 var styles = node.getAttribute("style").split(";");
22817 Roo.each(styles, function(s) {
22818 if (!s.match(/:/)) {
22821 var kv = s.split(":");
22822 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22825 // what ever is left... we allow.
22828 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22829 if (!nstyle.length) {
22830 node.removeAttribute('style');
22833 this.iterateChildren(node, this.cleanWord);
22839 * iterateChildren of a Node, calling fn each time, using this as the scole..
22840 * @param {DomNode} node node to iterate children of.
22841 * @param {Function} fn method of this class to call on each item.
22843 iterateChildren : function(node, fn)
22845 if (!node.childNodes.length) {
22848 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22849 fn.call(this, node.childNodes[i])
22855 * cleanTableWidths.
22857 * Quite often pasting from word etc.. results in tables with column and widths.
22858 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22861 cleanTableWidths : function(node)
22866 this.cleanTableWidths(this.doc.body);
22871 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22874 Roo.log(node.tagName);
22875 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22876 this.iterateChildren(node, this.cleanTableWidths);
22879 if (node.hasAttribute('width')) {
22880 node.removeAttribute('width');
22884 if (node.hasAttribute("style")) {
22887 var styles = node.getAttribute("style").split(";");
22889 Roo.each(styles, function(s) {
22890 if (!s.match(/:/)) {
22893 var kv = s.split(":");
22894 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22897 // what ever is left... we allow.
22900 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22901 if (!nstyle.length) {
22902 node.removeAttribute('style');
22906 this.iterateChildren(node, this.cleanTableWidths);
22914 domToHTML : function(currentElement, depth, nopadtext) {
22916 depth = depth || 0;
22917 nopadtext = nopadtext || false;
22919 if (!currentElement) {
22920 return this.domToHTML(this.doc.body);
22923 //Roo.log(currentElement);
22925 var allText = false;
22926 var nodeName = currentElement.nodeName;
22927 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22929 if (nodeName == '#text') {
22931 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22936 if (nodeName != 'BODY') {
22939 // Prints the node tagName, such as <A>, <IMG>, etc
22942 for(i = 0; i < currentElement.attributes.length;i++) {
22944 var aname = currentElement.attributes.item(i).name;
22945 if (!currentElement.attributes.item(i).value.length) {
22948 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22951 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22960 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22963 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22968 // Traverse the tree
22970 var currentElementChild = currentElement.childNodes.item(i);
22971 var allText = true;
22972 var innerHTML = '';
22974 while (currentElementChild) {
22975 // Formatting code (indent the tree so it looks nice on the screen)
22976 var nopad = nopadtext;
22977 if (lastnode == 'SPAN') {
22981 if (currentElementChild.nodeName == '#text') {
22982 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22983 toadd = nopadtext ? toadd : toadd.trim();
22984 if (!nopad && toadd.length > 80) {
22985 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22987 innerHTML += toadd;
22990 currentElementChild = currentElement.childNodes.item(i);
22996 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22998 // Recursively traverse the tree structure of the child node
22999 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23000 lastnode = currentElementChild.nodeName;
23002 currentElementChild=currentElement.childNodes.item(i);
23008 // The remaining code is mostly for formatting the tree
23009 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23014 ret+= "</"+tagName+">";
23020 applyBlacklists : function()
23022 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23023 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23027 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23028 if (b.indexOf(tag) > -1) {
23031 this.white.push(tag);
23035 Roo.each(w, function(tag) {
23036 if (b.indexOf(tag) > -1) {
23039 if (this.white.indexOf(tag) > -1) {
23042 this.white.push(tag);
23047 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23048 if (w.indexOf(tag) > -1) {
23051 this.black.push(tag);
23055 Roo.each(b, function(tag) {
23056 if (w.indexOf(tag) > -1) {
23059 if (this.black.indexOf(tag) > -1) {
23062 this.black.push(tag);
23067 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23068 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23072 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23073 if (b.indexOf(tag) > -1) {
23076 this.cwhite.push(tag);
23080 Roo.each(w, function(tag) {
23081 if (b.indexOf(tag) > -1) {
23084 if (this.cwhite.indexOf(tag) > -1) {
23087 this.cwhite.push(tag);
23092 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23093 if (w.indexOf(tag) > -1) {
23096 this.cblack.push(tag);
23100 Roo.each(b, function(tag) {
23101 if (w.indexOf(tag) > -1) {
23104 if (this.cblack.indexOf(tag) > -1) {
23107 this.cblack.push(tag);
23112 setStylesheets : function(stylesheets)
23114 if(typeof(stylesheets) == 'string'){
23115 Roo.get(this.iframe.contentDocument.head).createChild({
23117 rel : 'stylesheet',
23126 Roo.each(stylesheets, function(s) {
23131 Roo.get(_this.iframe.contentDocument.head).createChild({
23133 rel : 'stylesheet',
23142 removeStylesheets : function()
23146 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23151 setStyle : function(style)
23153 Roo.get(this.iframe.contentDocument.head).createChild({
23162 // hide stuff that is not compatible
23176 * @event specialkey
23180 * @cfg {String} fieldClass @hide
23183 * @cfg {String} focusClass @hide
23186 * @cfg {String} autoCreate @hide
23189 * @cfg {String} inputType @hide
23192 * @cfg {String} invalidClass @hide
23195 * @cfg {String} invalidText @hide
23198 * @cfg {String} msgFx @hide
23201 * @cfg {String} validateOnBlur @hide
23205 Roo.HtmlEditorCore.white = [
23206 'area', 'br', 'img', 'input', 'hr', 'wbr',
23208 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23209 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23210 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23211 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23212 'table', 'ul', 'xmp',
23214 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23217 'dir', 'menu', 'ol', 'ul', 'dl',
23223 Roo.HtmlEditorCore.black = [
23224 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23226 'base', 'basefont', 'bgsound', 'blink', 'body',
23227 'frame', 'frameset', 'head', 'html', 'ilayer',
23228 'iframe', 'layer', 'link', 'meta', 'object',
23229 'script', 'style' ,'title', 'xml' // clean later..
23231 Roo.HtmlEditorCore.clean = [
23232 'script', 'style', 'title', 'xml'
23234 Roo.HtmlEditorCore.remove = [
23239 Roo.HtmlEditorCore.ablack = [
23243 Roo.HtmlEditorCore.aclean = [
23244 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23248 Roo.HtmlEditorCore.pwhite= [
23249 'http', 'https', 'mailto'
23252 // white listed style attributes.
23253 Roo.HtmlEditorCore.cwhite= [
23254 // 'text-align', /// default is to allow most things..
23260 // black listed style attributes.
23261 Roo.HtmlEditorCore.cblack= [
23262 // 'font-size' -- this can be set by the project
23266 Roo.HtmlEditorCore.swapCodes =[
23285 * @class Roo.bootstrap.HtmlEditor
23286 * @extends Roo.bootstrap.TextArea
23287 * Bootstrap HtmlEditor class
23290 * Create a new HtmlEditor
23291 * @param {Object} config The config object
23294 Roo.bootstrap.HtmlEditor = function(config){
23295 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23296 if (!this.toolbars) {
23297 this.toolbars = [];
23300 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23303 * @event initialize
23304 * Fires when the editor is fully initialized (including the iframe)
23305 * @param {HtmlEditor} this
23310 * Fires when the editor is first receives the focus. Any insertion must wait
23311 * until after this event.
23312 * @param {HtmlEditor} this
23316 * @event beforesync
23317 * Fires before the textarea is updated with content from the editor iframe. Return false
23318 * to cancel the sync.
23319 * @param {HtmlEditor} this
23320 * @param {String} html
23324 * @event beforepush
23325 * Fires before the iframe editor is updated with content from the textarea. Return false
23326 * to cancel the push.
23327 * @param {HtmlEditor} this
23328 * @param {String} html
23333 * Fires when the textarea is updated with content from the editor iframe.
23334 * @param {HtmlEditor} this
23335 * @param {String} html
23340 * Fires when the iframe editor is updated with content from the textarea.
23341 * @param {HtmlEditor} this
23342 * @param {String} html
23346 * @event editmodechange
23347 * Fires when the editor switches edit modes
23348 * @param {HtmlEditor} this
23349 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23351 editmodechange: true,
23353 * @event editorevent
23354 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23355 * @param {HtmlEditor} this
23359 * @event firstfocus
23360 * Fires when on first focus - needed by toolbars..
23361 * @param {HtmlEditor} this
23366 * Auto save the htmlEditor value as a file into Events
23367 * @param {HtmlEditor} this
23371 * @event savedpreview
23372 * preview the saved version of htmlEditor
23373 * @param {HtmlEditor} this
23380 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23384 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23389 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23394 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23399 * @cfg {Number} height (in pixels)
23403 * @cfg {Number} width (in pixels)
23408 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23411 stylesheets: false,
23416 // private properties
23417 validationEvent : false,
23419 initialized : false,
23422 onFocus : Roo.emptyFn,
23424 hideMode:'offsets',
23426 tbContainer : false,
23430 toolbarContainer :function() {
23431 return this.wrap.select('.x-html-editor-tb',true).first();
23435 * Protected method that will not generally be called directly. It
23436 * is called when the editor creates its toolbar. Override this method if you need to
23437 * add custom toolbar buttons.
23438 * @param {HtmlEditor} editor
23440 createToolbar : function(){
23441 Roo.log('renewing');
23442 Roo.log("create toolbars");
23444 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23445 this.toolbars[0].render(this.toolbarContainer());
23449 // if (!editor.toolbars || !editor.toolbars.length) {
23450 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23453 // for (var i =0 ; i < editor.toolbars.length;i++) {
23454 // editor.toolbars[i] = Roo.factory(
23455 // typeof(editor.toolbars[i]) == 'string' ?
23456 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23457 // Roo.bootstrap.HtmlEditor);
23458 // editor.toolbars[i].init(editor);
23464 onRender : function(ct, position)
23466 // Roo.log("Call onRender: " + this.xtype);
23468 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23470 this.wrap = this.inputEl().wrap({
23471 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23474 this.editorcore.onRender(ct, position);
23476 if (this.resizable) {
23477 this.resizeEl = new Roo.Resizable(this.wrap, {
23481 minHeight : this.height,
23482 height: this.height,
23483 handles : this.resizable,
23486 resize : function(r, w, h) {
23487 _t.onResize(w,h); // -something
23493 this.createToolbar(this);
23496 if(!this.width && this.resizable){
23497 this.setSize(this.wrap.getSize());
23499 if (this.resizeEl) {
23500 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23501 // should trigger onReize..
23507 onResize : function(w, h)
23509 Roo.log('resize: ' +w + ',' + h );
23510 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23514 if(this.inputEl() ){
23515 if(typeof w == 'number'){
23516 var aw = w - this.wrap.getFrameWidth('lr');
23517 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23520 if(typeof h == 'number'){
23521 var tbh = -11; // fixme it needs to tool bar size!
23522 for (var i =0; i < this.toolbars.length;i++) {
23523 // fixme - ask toolbars for heights?
23524 tbh += this.toolbars[i].el.getHeight();
23525 //if (this.toolbars[i].footer) {
23526 // tbh += this.toolbars[i].footer.el.getHeight();
23534 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23535 ah -= 5; // knock a few pixes off for look..
23536 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23540 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23541 this.editorcore.onResize(ew,eh);
23546 * Toggles the editor between standard and source edit mode.
23547 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23549 toggleSourceEdit : function(sourceEditMode)
23551 this.editorcore.toggleSourceEdit(sourceEditMode);
23553 if(this.editorcore.sourceEditMode){
23554 Roo.log('editor - showing textarea');
23557 // Roo.log(this.syncValue());
23559 this.inputEl().removeClass(['hide', 'x-hidden']);
23560 this.inputEl().dom.removeAttribute('tabIndex');
23561 this.inputEl().focus();
23563 Roo.log('editor - hiding textarea');
23565 // Roo.log(this.pushValue());
23568 this.inputEl().addClass(['hide', 'x-hidden']);
23569 this.inputEl().dom.setAttribute('tabIndex', -1);
23570 //this.deferFocus();
23573 if(this.resizable){
23574 this.setSize(this.wrap.getSize());
23577 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23580 // private (for BoxComponent)
23581 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23583 // private (for BoxComponent)
23584 getResizeEl : function(){
23588 // private (for BoxComponent)
23589 getPositionEl : function(){
23594 initEvents : function(){
23595 this.originalValue = this.getValue();
23599 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23602 // markInvalid : Roo.emptyFn,
23604 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23607 // clearInvalid : Roo.emptyFn,
23609 setValue : function(v){
23610 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23611 this.editorcore.pushValue();
23616 deferFocus : function(){
23617 this.focus.defer(10, this);
23621 focus : function(){
23622 this.editorcore.focus();
23628 onDestroy : function(){
23634 for (var i =0; i < this.toolbars.length;i++) {
23635 // fixme - ask toolbars for heights?
23636 this.toolbars[i].onDestroy();
23639 this.wrap.dom.innerHTML = '';
23640 this.wrap.remove();
23645 onFirstFocus : function(){
23646 //Roo.log("onFirstFocus");
23647 this.editorcore.onFirstFocus();
23648 for (var i =0; i < this.toolbars.length;i++) {
23649 this.toolbars[i].onFirstFocus();
23655 syncValue : function()
23657 this.editorcore.syncValue();
23660 pushValue : function()
23662 this.editorcore.pushValue();
23666 // hide stuff that is not compatible
23680 * @event specialkey
23684 * @cfg {String} fieldClass @hide
23687 * @cfg {String} focusClass @hide
23690 * @cfg {String} autoCreate @hide
23693 * @cfg {String} inputType @hide
23696 * @cfg {String} invalidClass @hide
23699 * @cfg {String} invalidText @hide
23702 * @cfg {String} msgFx @hide
23705 * @cfg {String} validateOnBlur @hide
23714 Roo.namespace('Roo.bootstrap.htmleditor');
23716 * @class Roo.bootstrap.HtmlEditorToolbar1
23721 new Roo.bootstrap.HtmlEditor({
23724 new Roo.bootstrap.HtmlEditorToolbar1({
23725 disable : { fonts: 1 , format: 1, ..., ... , ...],
23731 * @cfg {Object} disable List of elements to disable..
23732 * @cfg {Array} btns List of additional buttons.
23736 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23739 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23742 Roo.apply(this, config);
23744 // default disabled, based on 'good practice'..
23745 this.disable = this.disable || {};
23746 Roo.applyIf(this.disable, {
23749 specialElements : true
23751 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23753 this.editor = config.editor;
23754 this.editorcore = config.editor.editorcore;
23756 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23758 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23759 // dont call parent... till later.
23761 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23766 editorcore : false,
23771 "h1","h2","h3","h4","h5","h6",
23773 "abbr", "acronym", "address", "cite", "samp", "var",
23777 onRender : function(ct, position)
23779 // Roo.log("Call onRender: " + this.xtype);
23781 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23783 this.el.dom.style.marginBottom = '0';
23785 var editorcore = this.editorcore;
23786 var editor= this.editor;
23789 var btn = function(id,cmd , toggle, handler, html){
23791 var event = toggle ? 'toggle' : 'click';
23796 xns: Roo.bootstrap,
23799 enableToggle:toggle !== false,
23801 pressed : toggle ? false : null,
23804 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23805 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23811 // var cb_box = function...
23816 xns: Roo.bootstrap,
23817 glyphicon : 'font',
23821 xns: Roo.bootstrap,
23825 Roo.each(this.formats, function(f) {
23826 style.menu.items.push({
23828 xns: Roo.bootstrap,
23829 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23834 editorcore.insertTag(this.tagname);
23841 children.push(style);
23843 btn('bold',false,true);
23844 btn('italic',false,true);
23845 btn('align-left', 'justifyleft',true);
23846 btn('align-center', 'justifycenter',true);
23847 btn('align-right' , 'justifyright',true);
23848 btn('link', false, false, function(btn) {
23849 //Roo.log("create link?");
23850 var url = prompt(this.createLinkText, this.defaultLinkValue);
23851 if(url && url != 'http:/'+'/'){
23852 this.editorcore.relayCmd('createlink', url);
23855 btn('list','insertunorderedlist',true);
23856 btn('pencil', false,true, function(btn){
23858 this.toggleSourceEdit(btn.pressed);
23861 if (this.editor.btns.length > 0) {
23862 for (var i = 0; i<this.editor.btns.length; i++) {
23863 children.push(this.editor.btns[i]);
23871 xns: Roo.bootstrap,
23876 xns: Roo.bootstrap,
23881 cog.menu.items.push({
23883 xns: Roo.bootstrap,
23884 html : Clean styles,
23889 editorcore.insertTag(this.tagname);
23898 this.xtype = 'NavSimplebar';
23900 for(var i=0;i< children.length;i++) {
23902 this.buttons.add(this.addxtypeChild(children[i]));
23906 editor.on('editorevent', this.updateToolbar, this);
23908 onBtnClick : function(id)
23910 this.editorcore.relayCmd(id);
23911 this.editorcore.focus();
23915 * Protected method that will not generally be called directly. It triggers
23916 * a toolbar update by reading the markup state of the current selection in the editor.
23918 updateToolbar: function(){
23920 if(!this.editorcore.activated){
23921 this.editor.onFirstFocus(); // is this neeed?
23925 var btns = this.buttons;
23926 var doc = this.editorcore.doc;
23927 btns.get('bold').setActive(doc.queryCommandState('bold'));
23928 btns.get('italic').setActive(doc.queryCommandState('italic'));
23929 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23931 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23932 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23933 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23935 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23936 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23939 var ans = this.editorcore.getAllAncestors();
23940 if (this.formatCombo) {
23943 var store = this.formatCombo.store;
23944 this.formatCombo.setValue("");
23945 for (var i =0; i < ans.length;i++) {
23946 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23948 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23956 // hides menus... - so this cant be on a menu...
23957 Roo.bootstrap.MenuMgr.hideAll();
23959 Roo.bootstrap.MenuMgr.hideAll();
23960 //this.editorsyncValue();
23962 onFirstFocus: function() {
23963 this.buttons.each(function(item){
23967 toggleSourceEdit : function(sourceEditMode){
23970 if(sourceEditMode){
23971 Roo.log("disabling buttons");
23972 this.buttons.each( function(item){
23973 if(item.cmd != 'pencil'){
23979 Roo.log("enabling buttons");
23980 if(this.editorcore.initialized){
23981 this.buttons.each( function(item){
23987 Roo.log("calling toggole on editor");
23988 // tell the editor that it's been pressed..
23989 this.editor.toggleSourceEdit(sourceEditMode);
23999 * @class Roo.bootstrap.Table.AbstractSelectionModel
24000 * @extends Roo.util.Observable
24001 * Abstract base class for grid SelectionModels. It provides the interface that should be
24002 * implemented by descendant classes. This class should not be directly instantiated.
24005 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24006 this.locked = false;
24007 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24011 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24012 /** @ignore Called by the grid automatically. Do not call directly. */
24013 init : function(grid){
24019 * Locks the selections.
24022 this.locked = true;
24026 * Unlocks the selections.
24028 unlock : function(){
24029 this.locked = false;
24033 * Returns true if the selections are locked.
24034 * @return {Boolean}
24036 isLocked : function(){
24037 return this.locked;
24041 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24042 * @class Roo.bootstrap.Table.RowSelectionModel
24043 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24044 * It supports multiple selections and keyboard selection/navigation.
24046 * @param {Object} config
24049 Roo.bootstrap.Table.RowSelectionModel = function(config){
24050 Roo.apply(this, config);
24051 this.selections = new Roo.util.MixedCollection(false, function(o){
24056 this.lastActive = false;
24060 * @event selectionchange
24061 * Fires when the selection changes
24062 * @param {SelectionModel} this
24064 "selectionchange" : true,
24066 * @event afterselectionchange
24067 * Fires after the selection changes (eg. by key press or clicking)
24068 * @param {SelectionModel} this
24070 "afterselectionchange" : true,
24072 * @event beforerowselect
24073 * Fires when a row is selected being selected, return false to cancel.
24074 * @param {SelectionModel} this
24075 * @param {Number} rowIndex The selected index
24076 * @param {Boolean} keepExisting False if other selections will be cleared
24078 "beforerowselect" : true,
24081 * Fires when a row is selected.
24082 * @param {SelectionModel} this
24083 * @param {Number} rowIndex The selected index
24084 * @param {Roo.data.Record} r The record
24086 "rowselect" : true,
24088 * @event rowdeselect
24089 * Fires when a row is deselected.
24090 * @param {SelectionModel} this
24091 * @param {Number} rowIndex The selected index
24093 "rowdeselect" : true
24095 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24096 this.locked = false;
24099 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24101 * @cfg {Boolean} singleSelect
24102 * True to allow selection of only one row at a time (defaults to false)
24104 singleSelect : false,
24107 initEvents : function()
24110 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24111 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24112 //}else{ // allow click to work like normal
24113 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24115 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24116 this.grid.on("rowclick", this.handleMouseDown, this);
24118 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24119 "up" : function(e){
24121 this.selectPrevious(e.shiftKey);
24122 }else if(this.last !== false && this.lastActive !== false){
24123 var last = this.last;
24124 this.selectRange(this.last, this.lastActive-1);
24125 this.grid.getView().focusRow(this.lastActive);
24126 if(last !== false){
24130 this.selectFirstRow();
24132 this.fireEvent("afterselectionchange", this);
24134 "down" : function(e){
24136 this.selectNext(e.shiftKey);
24137 }else if(this.last !== false && this.lastActive !== false){
24138 var last = this.last;
24139 this.selectRange(this.last, this.lastActive+1);
24140 this.grid.getView().focusRow(this.lastActive);
24141 if(last !== false){
24145 this.selectFirstRow();
24147 this.fireEvent("afterselectionchange", this);
24151 this.grid.store.on('load', function(){
24152 this.selections.clear();
24155 var view = this.grid.view;
24156 view.on("refresh", this.onRefresh, this);
24157 view.on("rowupdated", this.onRowUpdated, this);
24158 view.on("rowremoved", this.onRemove, this);
24163 onRefresh : function()
24165 var ds = this.grid.store, i, v = this.grid.view;
24166 var s = this.selections;
24167 s.each(function(r){
24168 if((i = ds.indexOfId(r.id)) != -1){
24177 onRemove : function(v, index, r){
24178 this.selections.remove(r);
24182 onRowUpdated : function(v, index, r){
24183 if(this.isSelected(r)){
24184 v.onRowSelect(index);
24190 * @param {Array} records The records to select
24191 * @param {Boolean} keepExisting (optional) True to keep existing selections
24193 selectRecords : function(records, keepExisting)
24196 this.clearSelections();
24198 var ds = this.grid.store;
24199 for(var i = 0, len = records.length; i < len; i++){
24200 this.selectRow(ds.indexOf(records[i]), true);
24205 * Gets the number of selected rows.
24208 getCount : function(){
24209 return this.selections.length;
24213 * Selects the first row in the grid.
24215 selectFirstRow : function(){
24220 * Select the last row.
24221 * @param {Boolean} keepExisting (optional) True to keep existing selections
24223 selectLastRow : function(keepExisting){
24224 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24225 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24229 * Selects the row immediately following the last selected row.
24230 * @param {Boolean} keepExisting (optional) True to keep existing selections
24232 selectNext : function(keepExisting)
24234 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24235 this.selectRow(this.last+1, keepExisting);
24236 this.grid.getView().focusRow(this.last);
24241 * Selects the row that precedes the last selected row.
24242 * @param {Boolean} keepExisting (optional) True to keep existing selections
24244 selectPrevious : function(keepExisting){
24246 this.selectRow(this.last-1, keepExisting);
24247 this.grid.getView().focusRow(this.last);
24252 * Returns the selected records
24253 * @return {Array} Array of selected records
24255 getSelections : function(){
24256 return [].concat(this.selections.items);
24260 * Returns the first selected record.
24263 getSelected : function(){
24264 return this.selections.itemAt(0);
24269 * Clears all selections.
24271 clearSelections : function(fast)
24277 var ds = this.grid.store;
24278 var s = this.selections;
24279 s.each(function(r){
24280 this.deselectRow(ds.indexOfId(r.id));
24284 this.selections.clear();
24291 * Selects all rows.
24293 selectAll : function(){
24297 this.selections.clear();
24298 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24299 this.selectRow(i, true);
24304 * Returns True if there is a selection.
24305 * @return {Boolean}
24307 hasSelection : function(){
24308 return this.selections.length > 0;
24312 * Returns True if the specified row is selected.
24313 * @param {Number/Record} record The record or index of the record to check
24314 * @return {Boolean}
24316 isSelected : function(index){
24317 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24318 return (r && this.selections.key(r.id) ? true : false);
24322 * Returns True if the specified record id is selected.
24323 * @param {String} id The id of record to check
24324 * @return {Boolean}
24326 isIdSelected : function(id){
24327 return (this.selections.key(id) ? true : false);
24332 handleMouseDBClick : function(e, t){
24336 handleMouseDown : function(e, t)
24338 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24339 if(this.isLocked() || rowIndex < 0 ){
24342 if(e.shiftKey && this.last !== false){
24343 var last = this.last;
24344 this.selectRange(last, rowIndex, e.ctrlKey);
24345 this.last = last; // reset the last
24349 var isSelected = this.isSelected(rowIndex);
24350 //Roo.log("select row:" + rowIndex);
24352 this.deselectRow(rowIndex);
24354 this.selectRow(rowIndex, true);
24358 if(e.button !== 0 && isSelected){
24359 alert('rowIndex 2: ' + rowIndex);
24360 view.focusRow(rowIndex);
24361 }else if(e.ctrlKey && isSelected){
24362 this.deselectRow(rowIndex);
24363 }else if(!isSelected){
24364 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24365 view.focusRow(rowIndex);
24369 this.fireEvent("afterselectionchange", this);
24372 handleDragableRowClick : function(grid, rowIndex, e)
24374 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24375 this.selectRow(rowIndex, false);
24376 grid.view.focusRow(rowIndex);
24377 this.fireEvent("afterselectionchange", this);
24382 * Selects multiple rows.
24383 * @param {Array} rows Array of the indexes of the row to select
24384 * @param {Boolean} keepExisting (optional) True to keep existing selections
24386 selectRows : function(rows, keepExisting){
24388 this.clearSelections();
24390 for(var i = 0, len = rows.length; i < len; i++){
24391 this.selectRow(rows[i], true);
24396 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24397 * @param {Number} startRow The index of the first row in the range
24398 * @param {Number} endRow The index of the last row in the range
24399 * @param {Boolean} keepExisting (optional) True to retain existing selections
24401 selectRange : function(startRow, endRow, keepExisting){
24406 this.clearSelections();
24408 if(startRow <= endRow){
24409 for(var i = startRow; i <= endRow; i++){
24410 this.selectRow(i, true);
24413 for(var i = startRow; i >= endRow; i--){
24414 this.selectRow(i, true);
24420 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24421 * @param {Number} startRow The index of the first row in the range
24422 * @param {Number} endRow The index of the last row in the range
24424 deselectRange : function(startRow, endRow, preventViewNotify){
24428 for(var i = startRow; i <= endRow; i++){
24429 this.deselectRow(i, preventViewNotify);
24435 * @param {Number} row The index of the row to select
24436 * @param {Boolean} keepExisting (optional) True to keep existing selections
24438 selectRow : function(index, keepExisting, preventViewNotify)
24440 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24443 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24444 if(!keepExisting || this.singleSelect){
24445 this.clearSelections();
24448 var r = this.grid.store.getAt(index);
24449 //console.log('selectRow - record id :' + r.id);
24451 this.selections.add(r);
24452 this.last = this.lastActive = index;
24453 if(!preventViewNotify){
24454 var proxy = new Roo.Element(
24455 this.grid.getRowDom(index)
24457 proxy.addClass('bg-info info');
24459 this.fireEvent("rowselect", this, index, r);
24460 this.fireEvent("selectionchange", this);
24466 * @param {Number} row The index of the row to deselect
24468 deselectRow : function(index, preventViewNotify)
24473 if(this.last == index){
24476 if(this.lastActive == index){
24477 this.lastActive = false;
24480 var r = this.grid.store.getAt(index);
24485 this.selections.remove(r);
24486 //.console.log('deselectRow - record id :' + r.id);
24487 if(!preventViewNotify){
24489 var proxy = new Roo.Element(
24490 this.grid.getRowDom(index)
24492 proxy.removeClass('bg-info info');
24494 this.fireEvent("rowdeselect", this, index);
24495 this.fireEvent("selectionchange", this);
24499 restoreLast : function(){
24501 this.last = this._last;
24506 acceptsNav : function(row, col, cm){
24507 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24511 onEditorKey : function(field, e){
24512 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24517 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24519 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24521 }else if(k == e.ENTER && !e.ctrlKey){
24525 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24527 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24529 }else if(k == e.ESC){
24533 g.startEditing(newCell[0], newCell[1]);
24539 * Ext JS Library 1.1.1
24540 * Copyright(c) 2006-2007, Ext JS, LLC.
24542 * Originally Released Under LGPL - original licence link has changed is not relivant.
24545 * <script type="text/javascript">
24549 * @class Roo.bootstrap.PagingToolbar
24550 * @extends Roo.bootstrap.NavSimplebar
24551 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24553 * Create a new PagingToolbar
24554 * @param {Object} config The config object
24555 * @param {Roo.data.Store} store
24557 Roo.bootstrap.PagingToolbar = function(config)
24559 // old args format still supported... - xtype is prefered..
24560 // created from xtype...
24562 this.ds = config.dataSource;
24564 if (config.store && !this.ds) {
24565 this.store= Roo.factory(config.store, Roo.data);
24566 this.ds = this.store;
24567 this.ds.xmodule = this.xmodule || false;
24570 this.toolbarItems = [];
24571 if (config.items) {
24572 this.toolbarItems = config.items;
24575 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24580 this.bind(this.ds);
24583 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24587 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24589 * @cfg {Roo.data.Store} dataSource
24590 * The underlying data store providing the paged data
24593 * @cfg {String/HTMLElement/Element} container
24594 * container The id or element that will contain the toolbar
24597 * @cfg {Boolean} displayInfo
24598 * True to display the displayMsg (defaults to false)
24601 * @cfg {Number} pageSize
24602 * The number of records to display per page (defaults to 20)
24606 * @cfg {String} displayMsg
24607 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24609 displayMsg : 'Displaying {0} - {1} of {2}',
24611 * @cfg {String} emptyMsg
24612 * The message to display when no records are found (defaults to "No data to display")
24614 emptyMsg : 'No data to display',
24616 * Customizable piece of the default paging text (defaults to "Page")
24619 beforePageText : "Page",
24621 * Customizable piece of the default paging text (defaults to "of %0")
24624 afterPageText : "of {0}",
24626 * Customizable piece of the default paging text (defaults to "First Page")
24629 firstText : "First Page",
24631 * Customizable piece of the default paging text (defaults to "Previous Page")
24634 prevText : "Previous Page",
24636 * Customizable piece of the default paging text (defaults to "Next Page")
24639 nextText : "Next Page",
24641 * Customizable piece of the default paging text (defaults to "Last Page")
24644 lastText : "Last Page",
24646 * Customizable piece of the default paging text (defaults to "Refresh")
24649 refreshText : "Refresh",
24653 onRender : function(ct, position)
24655 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24656 this.navgroup.parentId = this.id;
24657 this.navgroup.onRender(this.el, null);
24658 // add the buttons to the navgroup
24660 if(this.displayInfo){
24661 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24662 this.displayEl = this.el.select('.x-paging-info', true).first();
24663 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24664 // this.displayEl = navel.el.select('span',true).first();
24670 Roo.each(_this.buttons, function(e){ // this might need to use render????
24671 Roo.factory(e).render(_this.el);
24675 Roo.each(_this.toolbarItems, function(e) {
24676 _this.navgroup.addItem(e);
24680 this.first = this.navgroup.addItem({
24681 tooltip: this.firstText,
24683 icon : 'fa fa-backward',
24685 preventDefault: true,
24686 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24689 this.prev = this.navgroup.addItem({
24690 tooltip: this.prevText,
24692 icon : 'fa fa-step-backward',
24694 preventDefault: true,
24695 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24697 //this.addSeparator();
24700 var field = this.navgroup.addItem( {
24702 cls : 'x-paging-position',
24704 html : this.beforePageText +
24705 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24706 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24709 this.field = field.el.select('input', true).first();
24710 this.field.on("keydown", this.onPagingKeydown, this);
24711 this.field.on("focus", function(){this.dom.select();});
24714 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24715 //this.field.setHeight(18);
24716 //this.addSeparator();
24717 this.next = this.navgroup.addItem({
24718 tooltip: this.nextText,
24720 html : ' <i class="fa fa-step-forward">',
24722 preventDefault: true,
24723 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24725 this.last = this.navgroup.addItem({
24726 tooltip: this.lastText,
24727 icon : 'fa fa-forward',
24730 preventDefault: true,
24731 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24733 //this.addSeparator();
24734 this.loading = this.navgroup.addItem({
24735 tooltip: this.refreshText,
24736 icon: 'fa fa-refresh',
24737 preventDefault: true,
24738 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24744 updateInfo : function(){
24745 if(this.displayEl){
24746 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24747 var msg = count == 0 ?
24751 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24753 this.displayEl.update(msg);
24758 onLoad : function(ds, r, o)
24760 this.cursor = o.params.start ? o.params.start : 0;
24762 var d = this.getPageData(),
24767 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24768 this.field.dom.value = ap;
24769 this.first.setDisabled(ap == 1);
24770 this.prev.setDisabled(ap == 1);
24771 this.next.setDisabled(ap == ps);
24772 this.last.setDisabled(ap == ps);
24773 this.loading.enable();
24778 getPageData : function(){
24779 var total = this.ds.getTotalCount();
24782 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24783 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24788 onLoadError : function(){
24789 this.loading.enable();
24793 onPagingKeydown : function(e){
24794 var k = e.getKey();
24795 var d = this.getPageData();
24797 var v = this.field.dom.value, pageNum;
24798 if(!v || isNaN(pageNum = parseInt(v, 10))){
24799 this.field.dom.value = d.activePage;
24802 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24803 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24806 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))
24808 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24809 this.field.dom.value = pageNum;
24810 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24813 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24815 var v = this.field.dom.value, pageNum;
24816 var increment = (e.shiftKey) ? 10 : 1;
24817 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24820 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24821 this.field.dom.value = d.activePage;
24824 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24826 this.field.dom.value = parseInt(v, 10) + increment;
24827 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24828 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24835 beforeLoad : function(){
24837 this.loading.disable();
24842 onClick : function(which){
24851 ds.load({params:{start: 0, limit: this.pageSize}});
24854 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24857 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24860 var total = ds.getTotalCount();
24861 var extra = total % this.pageSize;
24862 var lastStart = extra ? (total - extra) : total-this.pageSize;
24863 ds.load({params:{start: lastStart, limit: this.pageSize}});
24866 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24872 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24873 * @param {Roo.data.Store} store The data store to unbind
24875 unbind : function(ds){
24876 ds.un("beforeload", this.beforeLoad, this);
24877 ds.un("load", this.onLoad, this);
24878 ds.un("loadexception", this.onLoadError, this);
24879 ds.un("remove", this.updateInfo, this);
24880 ds.un("add", this.updateInfo, this);
24881 this.ds = undefined;
24885 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24886 * @param {Roo.data.Store} store The data store to bind
24888 bind : function(ds){
24889 ds.on("beforeload", this.beforeLoad, this);
24890 ds.on("load", this.onLoad, this);
24891 ds.on("loadexception", this.onLoadError, this);
24892 ds.on("remove", this.updateInfo, this);
24893 ds.on("add", this.updateInfo, this);
24904 * @class Roo.bootstrap.MessageBar
24905 * @extends Roo.bootstrap.Component
24906 * Bootstrap MessageBar class
24907 * @cfg {String} html contents of the MessageBar
24908 * @cfg {String} weight (info | success | warning | danger) default info
24909 * @cfg {String} beforeClass insert the bar before the given class
24910 * @cfg {Boolean} closable (true | false) default false
24911 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24914 * Create a new Element
24915 * @param {Object} config The config object
24918 Roo.bootstrap.MessageBar = function(config){
24919 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24922 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24928 beforeClass: 'bootstrap-sticky-wrap',
24930 getAutoCreate : function(){
24934 cls: 'alert alert-dismissable alert-' + this.weight,
24939 html: this.html || ''
24945 cfg.cls += ' alert-messages-fixed';
24959 onRender : function(ct, position)
24961 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24964 var cfg = Roo.apply({}, this.getAutoCreate());
24968 cfg.cls += ' ' + this.cls;
24971 cfg.style = this.style;
24973 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24975 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24978 this.el.select('>button.close').on('click', this.hide, this);
24984 if (!this.rendered) {
24990 this.fireEvent('show', this);
24996 if (!this.rendered) {
25002 this.fireEvent('hide', this);
25005 update : function()
25007 // var e = this.el.dom.firstChild;
25009 // if(this.closable){
25010 // e = e.nextSibling;
25013 // e.data = this.html || '';
25015 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25031 * @class Roo.bootstrap.Graph
25032 * @extends Roo.bootstrap.Component
25033 * Bootstrap Graph class
25037 @cfg {String} graphtype bar | vbar | pie
25038 @cfg {number} g_x coodinator | centre x (pie)
25039 @cfg {number} g_y coodinator | centre y (pie)
25040 @cfg {number} g_r radius (pie)
25041 @cfg {number} g_height height of the chart (respected by all elements in the set)
25042 @cfg {number} g_width width of the chart (respected by all elements in the set)
25043 @cfg {Object} title The title of the chart
25046 -opts (object) options for the chart
25048 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25049 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25051 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.
25052 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25054 o stretch (boolean)
25056 -opts (object) options for the pie
25059 o startAngle (number)
25060 o endAngle (number)
25064 * Create a new Input
25065 * @param {Object} config The config object
25068 Roo.bootstrap.Graph = function(config){
25069 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25075 * The img click event for the img.
25076 * @param {Roo.EventObject} e
25082 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25093 //g_colors: this.colors,
25100 getAutoCreate : function(){
25111 onRender : function(ct,position){
25114 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25116 if (typeof(Raphael) == 'undefined') {
25117 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25121 this.raphael = Raphael(this.el.dom);
25123 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25124 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25125 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25126 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25128 r.text(160, 10, "Single Series Chart").attr(txtattr);
25129 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25130 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25131 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25133 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25134 r.barchart(330, 10, 300, 220, data1);
25135 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25136 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25139 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25140 // r.barchart(30, 30, 560, 250, xdata, {
25141 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25142 // axis : "0 0 1 1",
25143 // axisxlabels : xdata
25144 // //yvalues : cols,
25147 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25149 // this.load(null,xdata,{
25150 // axis : "0 0 1 1",
25151 // axisxlabels : xdata
25156 load : function(graphtype,xdata,opts)
25158 this.raphael.clear();
25160 graphtype = this.graphtype;
25165 var r = this.raphael,
25166 fin = function () {
25167 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25169 fout = function () {
25170 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25172 pfin = function() {
25173 this.sector.stop();
25174 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25177 this.label[0].stop();
25178 this.label[0].attr({ r: 7.5 });
25179 this.label[1].attr({ "font-weight": 800 });
25182 pfout = function() {
25183 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25186 this.label[0].animate({ r: 5 }, 500, "bounce");
25187 this.label[1].attr({ "font-weight": 400 });
25193 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25196 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25199 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25200 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25202 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25209 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25214 setTitle: function(o)
25219 initEvents: function() {
25222 this.el.on('click', this.onClick, this);
25226 onClick : function(e)
25228 Roo.log('img onclick');
25229 this.fireEvent('click', this, e);
25241 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25244 * @class Roo.bootstrap.dash.NumberBox
25245 * @extends Roo.bootstrap.Component
25246 * Bootstrap NumberBox class
25247 * @cfg {String} headline Box headline
25248 * @cfg {String} content Box content
25249 * @cfg {String} icon Box icon
25250 * @cfg {String} footer Footer text
25251 * @cfg {String} fhref Footer href
25254 * Create a new NumberBox
25255 * @param {Object} config The config object
25259 Roo.bootstrap.dash.NumberBox = function(config){
25260 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25264 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25273 getAutoCreate : function(){
25277 cls : 'small-box ',
25285 cls : 'roo-headline',
25286 html : this.headline
25290 cls : 'roo-content',
25291 html : this.content
25305 cls : 'ion ' + this.icon
25314 cls : 'small-box-footer',
25315 href : this.fhref || '#',
25319 cfg.cn.push(footer);
25326 onRender : function(ct,position){
25327 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25334 setHeadline: function (value)
25336 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25339 setFooter: function (value, href)
25341 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25344 this.el.select('a.small-box-footer',true).first().attr('href', href);
25349 setContent: function (value)
25351 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25354 initEvents: function()
25368 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25371 * @class Roo.bootstrap.dash.TabBox
25372 * @extends Roo.bootstrap.Component
25373 * Bootstrap TabBox class
25374 * @cfg {String} title Title of the TabBox
25375 * @cfg {String} icon Icon of the TabBox
25376 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25377 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25380 * Create a new TabBox
25381 * @param {Object} config The config object
25385 Roo.bootstrap.dash.TabBox = function(config){
25386 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25391 * When a pane is added
25392 * @param {Roo.bootstrap.dash.TabPane} pane
25396 * @event activatepane
25397 * When a pane is activated
25398 * @param {Roo.bootstrap.dash.TabPane} pane
25400 "activatepane" : true
25408 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25413 tabScrollable : false,
25415 getChildContainer : function()
25417 return this.el.select('.tab-content', true).first();
25420 getAutoCreate : function(){
25424 cls: 'pull-left header',
25432 cls: 'fa ' + this.icon
25438 cls: 'nav nav-tabs pull-right',
25444 if(this.tabScrollable){
25451 cls: 'nav nav-tabs pull-right',
25462 cls: 'nav-tabs-custom',
25467 cls: 'tab-content no-padding',
25475 initEvents : function()
25477 //Roo.log('add add pane handler');
25478 this.on('addpane', this.onAddPane, this);
25481 * Updates the box title
25482 * @param {String} html to set the title to.
25484 setTitle : function(value)
25486 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25488 onAddPane : function(pane)
25490 this.panes.push(pane);
25491 //Roo.log('addpane');
25493 // tabs are rendere left to right..
25494 if(!this.showtabs){
25498 var ctr = this.el.select('.nav-tabs', true).first();
25501 var existing = ctr.select('.nav-tab',true);
25502 var qty = existing.getCount();;
25505 var tab = ctr.createChild({
25507 cls : 'nav-tab' + (qty ? '' : ' active'),
25515 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25518 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25520 pane.el.addClass('active');
25525 onTabClick : function(ev,un,ob,pane)
25527 //Roo.log('tab - prev default');
25528 ev.preventDefault();
25531 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25532 pane.tab.addClass('active');
25533 //Roo.log(pane.title);
25534 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25535 // technically we should have a deactivate event.. but maybe add later.
25536 // and it should not de-activate the selected tab...
25537 this.fireEvent('activatepane', pane);
25538 pane.el.addClass('active');
25539 pane.fireEvent('activate');
25544 getActivePane : function()
25547 Roo.each(this.panes, function(p) {
25548 if(p.el.hasClass('active')){
25569 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25571 * @class Roo.bootstrap.TabPane
25572 * @extends Roo.bootstrap.Component
25573 * Bootstrap TabPane class
25574 * @cfg {Boolean} active (false | true) Default false
25575 * @cfg {String} title title of panel
25579 * Create a new TabPane
25580 * @param {Object} config The config object
25583 Roo.bootstrap.dash.TabPane = function(config){
25584 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25590 * When a pane is activated
25591 * @param {Roo.bootstrap.dash.TabPane} pane
25598 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25603 // the tabBox that this is attached to.
25606 getAutoCreate : function()
25614 cfg.cls += ' active';
25619 initEvents : function()
25621 //Roo.log('trigger add pane handler');
25622 this.parent().fireEvent('addpane', this)
25626 * Updates the tab title
25627 * @param {String} html to set the title to.
25629 setTitle: function(str)
25635 this.tab.select('a', true).first().dom.innerHTML = str;
25652 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25655 * @class Roo.bootstrap.menu.Menu
25656 * @extends Roo.bootstrap.Component
25657 * Bootstrap Menu class - container for Menu
25658 * @cfg {String} html Text of the menu
25659 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25660 * @cfg {String} icon Font awesome icon
25661 * @cfg {String} pos Menu align to (top | bottom) default bottom
25665 * Create a new Menu
25666 * @param {Object} config The config object
25670 Roo.bootstrap.menu.Menu = function(config){
25671 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25675 * @event beforeshow
25676 * Fires before this menu is displayed
25677 * @param {Roo.bootstrap.menu.Menu} this
25681 * @event beforehide
25682 * Fires before this menu is hidden
25683 * @param {Roo.bootstrap.menu.Menu} this
25688 * Fires after this menu is displayed
25689 * @param {Roo.bootstrap.menu.Menu} this
25694 * Fires after this menu is hidden
25695 * @param {Roo.bootstrap.menu.Menu} this
25700 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25701 * @param {Roo.bootstrap.menu.Menu} this
25702 * @param {Roo.EventObject} e
25709 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25713 weight : 'default',
25718 getChildContainer : function() {
25719 if(this.isSubMenu){
25723 return this.el.select('ul.dropdown-menu', true).first();
25726 getAutoCreate : function()
25731 cls : 'roo-menu-text',
25739 cls : 'fa ' + this.icon
25750 cls : 'dropdown-button btn btn-' + this.weight,
25755 cls : 'dropdown-toggle btn btn-' + this.weight,
25765 cls : 'dropdown-menu'
25771 if(this.pos == 'top'){
25772 cfg.cls += ' dropup';
25775 if(this.isSubMenu){
25778 cls : 'dropdown-menu'
25785 onRender : function(ct, position)
25787 this.isSubMenu = ct.hasClass('dropdown-submenu');
25789 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25792 initEvents : function()
25794 if(this.isSubMenu){
25798 this.hidden = true;
25800 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25801 this.triggerEl.on('click', this.onTriggerPress, this);
25803 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25804 this.buttonEl.on('click', this.onClick, this);
25810 if(this.isSubMenu){
25814 return this.el.select('ul.dropdown-menu', true).first();
25817 onClick : function(e)
25819 this.fireEvent("click", this, e);
25822 onTriggerPress : function(e)
25824 if (this.isVisible()) {
25831 isVisible : function(){
25832 return !this.hidden;
25837 this.fireEvent("beforeshow", this);
25839 this.hidden = false;
25840 this.el.addClass('open');
25842 Roo.get(document).on("mouseup", this.onMouseUp, this);
25844 this.fireEvent("show", this);
25851 this.fireEvent("beforehide", this);
25853 this.hidden = true;
25854 this.el.removeClass('open');
25856 Roo.get(document).un("mouseup", this.onMouseUp);
25858 this.fireEvent("hide", this);
25861 onMouseUp : function()
25875 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25878 * @class Roo.bootstrap.menu.Item
25879 * @extends Roo.bootstrap.Component
25880 * Bootstrap MenuItem class
25881 * @cfg {Boolean} submenu (true | false) default false
25882 * @cfg {String} html text of the item
25883 * @cfg {String} href the link
25884 * @cfg {Boolean} disable (true | false) default false
25885 * @cfg {Boolean} preventDefault (true | false) default true
25886 * @cfg {String} icon Font awesome icon
25887 * @cfg {String} pos Submenu align to (left | right) default right
25891 * Create a new Item
25892 * @param {Object} config The config object
25896 Roo.bootstrap.menu.Item = function(config){
25897 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25901 * Fires when the mouse is hovering over this menu
25902 * @param {Roo.bootstrap.menu.Item} this
25903 * @param {Roo.EventObject} e
25908 * Fires when the mouse exits this menu
25909 * @param {Roo.bootstrap.menu.Item} this
25910 * @param {Roo.EventObject} e
25916 * The raw click event for the entire grid.
25917 * @param {Roo.EventObject} e
25923 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25928 preventDefault: true,
25933 getAutoCreate : function()
25938 cls : 'roo-menu-item-text',
25946 cls : 'fa ' + this.icon
25955 href : this.href || '#',
25962 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25966 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25968 if(this.pos == 'left'){
25969 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25976 initEvents : function()
25978 this.el.on('mouseover', this.onMouseOver, this);
25979 this.el.on('mouseout', this.onMouseOut, this);
25981 this.el.select('a', true).first().on('click', this.onClick, this);
25985 onClick : function(e)
25987 if(this.preventDefault){
25988 e.preventDefault();
25991 this.fireEvent("click", this, e);
25994 onMouseOver : function(e)
25996 if(this.submenu && this.pos == 'left'){
25997 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26000 this.fireEvent("mouseover", this, e);
26003 onMouseOut : function(e)
26005 this.fireEvent("mouseout", this, e);
26017 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26020 * @class Roo.bootstrap.menu.Separator
26021 * @extends Roo.bootstrap.Component
26022 * Bootstrap Separator class
26025 * Create a new Separator
26026 * @param {Object} config The config object
26030 Roo.bootstrap.menu.Separator = function(config){
26031 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26034 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26036 getAutoCreate : function(){
26057 * @class Roo.bootstrap.Tooltip
26058 * Bootstrap Tooltip class
26059 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26060 * to determine which dom element triggers the tooltip.
26062 * It needs to add support for additional attributes like tooltip-position
26065 * Create a new Toolti
26066 * @param {Object} config The config object
26069 Roo.bootstrap.Tooltip = function(config){
26070 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26072 this.alignment = Roo.bootstrap.Tooltip.alignment;
26074 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26075 this.alignment = config.alignment;
26080 Roo.apply(Roo.bootstrap.Tooltip, {
26082 * @function init initialize tooltip monitoring.
26086 currentTip : false,
26087 currentRegion : false,
26093 Roo.get(document).on('mouseover', this.enter ,this);
26094 Roo.get(document).on('mouseout', this.leave, this);
26097 this.currentTip = new Roo.bootstrap.Tooltip();
26100 enter : function(ev)
26102 var dom = ev.getTarget();
26104 //Roo.log(['enter',dom]);
26105 var el = Roo.fly(dom);
26106 if (this.currentEl) {
26108 //Roo.log(this.currentEl);
26109 //Roo.log(this.currentEl.contains(dom));
26110 if (this.currentEl == el) {
26113 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26119 if (this.currentTip.el) {
26120 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26124 if(!el || el.dom == document){
26130 // you can not look for children, as if el is the body.. then everythign is the child..
26131 if (!el.attr('tooltip')) { //
26132 if (!el.select("[tooltip]").elements.length) {
26135 // is the mouse over this child...?
26136 bindEl = el.select("[tooltip]").first();
26137 var xy = ev.getXY();
26138 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26139 //Roo.log("not in region.");
26142 //Roo.log("child element over..");
26145 this.currentEl = bindEl;
26146 this.currentTip.bind(bindEl);
26147 this.currentRegion = Roo.lib.Region.getRegion(dom);
26148 this.currentTip.enter();
26151 leave : function(ev)
26153 var dom = ev.getTarget();
26154 //Roo.log(['leave',dom]);
26155 if (!this.currentEl) {
26160 if (dom != this.currentEl.dom) {
26163 var xy = ev.getXY();
26164 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26167 // only activate leave if mouse cursor is outside... bounding box..
26172 if (this.currentTip) {
26173 this.currentTip.leave();
26175 //Roo.log('clear currentEl');
26176 this.currentEl = false;
26181 'left' : ['r-l', [-2,0], 'right'],
26182 'right' : ['l-r', [2,0], 'left'],
26183 'bottom' : ['t-b', [0,2], 'top'],
26184 'top' : [ 'b-t', [0,-2], 'bottom']
26190 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26195 delay : null, // can be { show : 300 , hide: 500}
26199 hoverState : null, //???
26201 placement : 'bottom',
26205 getAutoCreate : function(){
26212 cls : 'tooltip-arrow'
26215 cls : 'tooltip-inner'
26222 bind : function(el)
26228 enter : function () {
26230 if (this.timeout != null) {
26231 clearTimeout(this.timeout);
26234 this.hoverState = 'in';
26235 //Roo.log("enter - show");
26236 if (!this.delay || !this.delay.show) {
26241 this.timeout = setTimeout(function () {
26242 if (_t.hoverState == 'in') {
26245 }, this.delay.show);
26249 clearTimeout(this.timeout);
26251 this.hoverState = 'out';
26252 if (!this.delay || !this.delay.hide) {
26258 this.timeout = setTimeout(function () {
26259 //Roo.log("leave - timeout");
26261 if (_t.hoverState == 'out') {
26263 Roo.bootstrap.Tooltip.currentEl = false;
26268 show : function (msg)
26271 this.render(document.body);
26274 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26276 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26278 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26280 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26282 var placement = typeof this.placement == 'function' ?
26283 this.placement.call(this, this.el, on_el) :
26286 var autoToken = /\s?auto?\s?/i;
26287 var autoPlace = autoToken.test(placement);
26289 placement = placement.replace(autoToken, '') || 'top';
26293 //this.el.setXY([0,0]);
26295 //this.el.dom.style.display='block';
26297 //this.el.appendTo(on_el);
26299 var p = this.getPosition();
26300 var box = this.el.getBox();
26306 var align = this.alignment[placement];
26308 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26310 if(placement == 'top' || placement == 'bottom'){
26312 placement = 'right';
26315 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26316 placement = 'left';
26319 var scroll = Roo.select('body', true).first().getScroll();
26321 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26325 align = this.alignment[placement];
26328 this.el.alignTo(this.bindEl, align[0],align[1]);
26329 //var arrow = this.el.select('.arrow',true).first();
26330 //arrow.set(align[2],
26332 this.el.addClass(placement);
26334 this.el.addClass('in fade');
26336 this.hoverState = null;
26338 if (this.el.hasClass('fade')) {
26349 //this.el.setXY([0,0]);
26350 this.el.removeClass('in');
26366 * @class Roo.bootstrap.LocationPicker
26367 * @extends Roo.bootstrap.Component
26368 * Bootstrap LocationPicker class
26369 * @cfg {Number} latitude Position when init default 0
26370 * @cfg {Number} longitude Position when init default 0
26371 * @cfg {Number} zoom default 15
26372 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26373 * @cfg {Boolean} mapTypeControl default false
26374 * @cfg {Boolean} disableDoubleClickZoom default false
26375 * @cfg {Boolean} scrollwheel default true
26376 * @cfg {Boolean} streetViewControl default false
26377 * @cfg {Number} radius default 0
26378 * @cfg {String} locationName
26379 * @cfg {Boolean} draggable default true
26380 * @cfg {Boolean} enableAutocomplete default false
26381 * @cfg {Boolean} enableReverseGeocode default true
26382 * @cfg {String} markerTitle
26385 * Create a new LocationPicker
26386 * @param {Object} config The config object
26390 Roo.bootstrap.LocationPicker = function(config){
26392 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26397 * Fires when the picker initialized.
26398 * @param {Roo.bootstrap.LocationPicker} this
26399 * @param {Google Location} location
26403 * @event positionchanged
26404 * Fires when the picker position changed.
26405 * @param {Roo.bootstrap.LocationPicker} this
26406 * @param {Google Location} location
26408 positionchanged : true,
26411 * Fires when the map resize.
26412 * @param {Roo.bootstrap.LocationPicker} this
26417 * Fires when the map show.
26418 * @param {Roo.bootstrap.LocationPicker} this
26423 * Fires when the map hide.
26424 * @param {Roo.bootstrap.LocationPicker} this
26429 * Fires when click the map.
26430 * @param {Roo.bootstrap.LocationPicker} this
26431 * @param {Map event} e
26435 * @event mapRightClick
26436 * Fires when right click the map.
26437 * @param {Roo.bootstrap.LocationPicker} this
26438 * @param {Map event} e
26440 mapRightClick : true,
26442 * @event markerClick
26443 * Fires when click the marker.
26444 * @param {Roo.bootstrap.LocationPicker} this
26445 * @param {Map event} e
26447 markerClick : true,
26449 * @event markerRightClick
26450 * Fires when right click the marker.
26451 * @param {Roo.bootstrap.LocationPicker} this
26452 * @param {Map event} e
26454 markerRightClick : true,
26456 * @event OverlayViewDraw
26457 * Fires when OverlayView Draw
26458 * @param {Roo.bootstrap.LocationPicker} this
26460 OverlayViewDraw : true,
26462 * @event OverlayViewOnAdd
26463 * Fires when OverlayView Draw
26464 * @param {Roo.bootstrap.LocationPicker} this
26466 OverlayViewOnAdd : true,
26468 * @event OverlayViewOnRemove
26469 * Fires when OverlayView Draw
26470 * @param {Roo.bootstrap.LocationPicker} this
26472 OverlayViewOnRemove : true,
26474 * @event OverlayViewShow
26475 * Fires when OverlayView Draw
26476 * @param {Roo.bootstrap.LocationPicker} this
26477 * @param {Pixel} cpx
26479 OverlayViewShow : true,
26481 * @event OverlayViewHide
26482 * Fires when OverlayView Draw
26483 * @param {Roo.bootstrap.LocationPicker} this
26485 OverlayViewHide : true,
26487 * @event loadexception
26488 * Fires when load google lib failed.
26489 * @param {Roo.bootstrap.LocationPicker} this
26491 loadexception : true
26496 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26498 gMapContext: false,
26504 mapTypeControl: false,
26505 disableDoubleClickZoom: false,
26507 streetViewControl: false,
26511 enableAutocomplete: false,
26512 enableReverseGeocode: true,
26515 getAutoCreate: function()
26520 cls: 'roo-location-picker'
26526 initEvents: function(ct, position)
26528 if(!this.el.getWidth() || this.isApplied()){
26532 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26537 initial: function()
26539 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26540 this.fireEvent('loadexception', this);
26544 if(!this.mapTypeId){
26545 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26548 this.gMapContext = this.GMapContext();
26550 this.initOverlayView();
26552 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26556 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26557 _this.setPosition(_this.gMapContext.marker.position);
26560 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26561 _this.fireEvent('mapClick', this, event);
26565 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26566 _this.fireEvent('mapRightClick', this, event);
26570 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26571 _this.fireEvent('markerClick', this, event);
26575 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26576 _this.fireEvent('markerRightClick', this, event);
26580 this.setPosition(this.gMapContext.location);
26582 this.fireEvent('initial', this, this.gMapContext.location);
26585 initOverlayView: function()
26589 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26593 _this.fireEvent('OverlayViewDraw', _this);
26598 _this.fireEvent('OverlayViewOnAdd', _this);
26601 onRemove: function()
26603 _this.fireEvent('OverlayViewOnRemove', _this);
26606 show: function(cpx)
26608 _this.fireEvent('OverlayViewShow', _this, cpx);
26613 _this.fireEvent('OverlayViewHide', _this);
26619 fromLatLngToContainerPixel: function(event)
26621 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26624 isApplied: function()
26626 return this.getGmapContext() == false ? false : true;
26629 getGmapContext: function()
26631 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26634 GMapContext: function()
26636 var position = new google.maps.LatLng(this.latitude, this.longitude);
26638 var _map = new google.maps.Map(this.el.dom, {
26641 mapTypeId: this.mapTypeId,
26642 mapTypeControl: this.mapTypeControl,
26643 disableDoubleClickZoom: this.disableDoubleClickZoom,
26644 scrollwheel: this.scrollwheel,
26645 streetViewControl: this.streetViewControl,
26646 locationName: this.locationName,
26647 draggable: this.draggable,
26648 enableAutocomplete: this.enableAutocomplete,
26649 enableReverseGeocode: this.enableReverseGeocode
26652 var _marker = new google.maps.Marker({
26653 position: position,
26655 title: this.markerTitle,
26656 draggable: this.draggable
26663 location: position,
26664 radius: this.radius,
26665 locationName: this.locationName,
26666 addressComponents: {
26667 formatted_address: null,
26668 addressLine1: null,
26669 addressLine2: null,
26671 streetNumber: null,
26675 stateOrProvince: null
26678 domContainer: this.el.dom,
26679 geodecoder: new google.maps.Geocoder()
26683 drawCircle: function(center, radius, options)
26685 if (this.gMapContext.circle != null) {
26686 this.gMapContext.circle.setMap(null);
26690 options = Roo.apply({}, options, {
26691 strokeColor: "#0000FF",
26692 strokeOpacity: .35,
26694 fillColor: "#0000FF",
26698 options.map = this.gMapContext.map;
26699 options.radius = radius;
26700 options.center = center;
26701 this.gMapContext.circle = new google.maps.Circle(options);
26702 return this.gMapContext.circle;
26708 setPosition: function(location)
26710 this.gMapContext.location = location;
26711 this.gMapContext.marker.setPosition(location);
26712 this.gMapContext.map.panTo(location);
26713 this.drawCircle(location, this.gMapContext.radius, {});
26717 if (this.gMapContext.settings.enableReverseGeocode) {
26718 this.gMapContext.geodecoder.geocode({
26719 latLng: this.gMapContext.location
26720 }, function(results, status) {
26722 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26723 _this.gMapContext.locationName = results[0].formatted_address;
26724 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26726 _this.fireEvent('positionchanged', this, location);
26733 this.fireEvent('positionchanged', this, location);
26738 google.maps.event.trigger(this.gMapContext.map, "resize");
26740 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26742 this.fireEvent('resize', this);
26745 setPositionByLatLng: function(latitude, longitude)
26747 this.setPosition(new google.maps.LatLng(latitude, longitude));
26750 getCurrentPosition: function()
26753 latitude: this.gMapContext.location.lat(),
26754 longitude: this.gMapContext.location.lng()
26758 getAddressName: function()
26760 return this.gMapContext.locationName;
26763 getAddressComponents: function()
26765 return this.gMapContext.addressComponents;
26768 address_component_from_google_geocode: function(address_components)
26772 for (var i = 0; i < address_components.length; i++) {
26773 var component = address_components[i];
26774 if (component.types.indexOf("postal_code") >= 0) {
26775 result.postalCode = component.short_name;
26776 } else if (component.types.indexOf("street_number") >= 0) {
26777 result.streetNumber = component.short_name;
26778 } else if (component.types.indexOf("route") >= 0) {
26779 result.streetName = component.short_name;
26780 } else if (component.types.indexOf("neighborhood") >= 0) {
26781 result.city = component.short_name;
26782 } else if (component.types.indexOf("locality") >= 0) {
26783 result.city = component.short_name;
26784 } else if (component.types.indexOf("sublocality") >= 0) {
26785 result.district = component.short_name;
26786 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26787 result.stateOrProvince = component.short_name;
26788 } else if (component.types.indexOf("country") >= 0) {
26789 result.country = component.short_name;
26793 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26794 result.addressLine2 = "";
26798 setZoomLevel: function(zoom)
26800 this.gMapContext.map.setZoom(zoom);
26813 this.fireEvent('show', this);
26824 this.fireEvent('hide', this);
26829 Roo.apply(Roo.bootstrap.LocationPicker, {
26831 OverlayView : function(map, options)
26833 options = options || {};
26847 * @class Roo.bootstrap.Alert
26848 * @extends Roo.bootstrap.Component
26849 * Bootstrap Alert class
26850 * @cfg {String} title The title of alert
26851 * @cfg {String} html The content of alert
26852 * @cfg {String} weight ( success | info | warning | danger )
26853 * @cfg {String} faicon font-awesomeicon
26856 * Create a new alert
26857 * @param {Object} config The config object
26861 Roo.bootstrap.Alert = function(config){
26862 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26866 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26873 getAutoCreate : function()
26882 cls : 'roo-alert-icon'
26887 cls : 'roo-alert-title',
26892 cls : 'roo-alert-text',
26899 cfg.cn[0].cls += ' fa ' + this.faicon;
26903 cfg.cls += ' alert-' + this.weight;
26909 initEvents: function()
26911 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26914 setTitle : function(str)
26916 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26919 setText : function(str)
26921 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26924 setWeight : function(weight)
26927 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26930 this.weight = weight;
26932 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26935 setIcon : function(icon)
26938 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26941 this.faicon = icon;
26943 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26964 * @class Roo.bootstrap.UploadCropbox
26965 * @extends Roo.bootstrap.Component
26966 * Bootstrap UploadCropbox class
26967 * @cfg {String} emptyText show when image has been loaded
26968 * @cfg {String} rotateNotify show when image too small to rotate
26969 * @cfg {Number} errorTimeout default 3000
26970 * @cfg {Number} minWidth default 300
26971 * @cfg {Number} minHeight default 300
26972 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26973 * @cfg {Boolean} isDocument (true|false) default false
26974 * @cfg {String} url action url
26975 * @cfg {String} paramName default 'imageUpload'
26976 * @cfg {String} method default POST
26977 * @cfg {Boolean} loadMask (true|false) default true
26978 * @cfg {Boolean} loadingText default 'Loading...'
26981 * Create a new UploadCropbox
26982 * @param {Object} config The config object
26985 Roo.bootstrap.UploadCropbox = function(config){
26986 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26990 * @event beforeselectfile
26991 * Fire before select file
26992 * @param {Roo.bootstrap.UploadCropbox} this
26994 "beforeselectfile" : true,
26997 * Fire after initEvent
26998 * @param {Roo.bootstrap.UploadCropbox} this
27003 * Fire after initEvent
27004 * @param {Roo.bootstrap.UploadCropbox} this
27005 * @param {String} data
27010 * Fire when preparing the file data
27011 * @param {Roo.bootstrap.UploadCropbox} this
27012 * @param {Object} file
27017 * Fire when get exception
27018 * @param {Roo.bootstrap.UploadCropbox} this
27019 * @param {XMLHttpRequest} xhr
27021 "exception" : true,
27023 * @event beforeloadcanvas
27024 * Fire before load the canvas
27025 * @param {Roo.bootstrap.UploadCropbox} this
27026 * @param {String} src
27028 "beforeloadcanvas" : true,
27031 * Fire when trash image
27032 * @param {Roo.bootstrap.UploadCropbox} this
27037 * Fire when download the image
27038 * @param {Roo.bootstrap.UploadCropbox} this
27042 * @event footerbuttonclick
27043 * Fire when footerbuttonclick
27044 * @param {Roo.bootstrap.UploadCropbox} this
27045 * @param {String} type
27047 "footerbuttonclick" : true,
27051 * @param {Roo.bootstrap.UploadCropbox} this
27056 * Fire when rotate the image
27057 * @param {Roo.bootstrap.UploadCropbox} this
27058 * @param {String} pos
27063 * Fire when inspect the file
27064 * @param {Roo.bootstrap.UploadCropbox} this
27065 * @param {Object} file
27070 * Fire when xhr upload the file
27071 * @param {Roo.bootstrap.UploadCropbox} this
27072 * @param {Object} data
27077 * Fire when arrange the file data
27078 * @param {Roo.bootstrap.UploadCropbox} this
27079 * @param {Object} formData
27084 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27087 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27089 emptyText : 'Click to upload image',
27090 rotateNotify : 'Image is too small to rotate',
27091 errorTimeout : 3000,
27105 cropType : 'image/jpeg',
27107 canvasLoaded : false,
27108 isDocument : false,
27110 paramName : 'imageUpload',
27112 loadingText : 'Loading...',
27115 getAutoCreate : function()
27119 cls : 'roo-upload-cropbox',
27123 cls : 'roo-upload-cropbox-selector',
27128 cls : 'roo-upload-cropbox-body',
27129 style : 'cursor:pointer',
27133 cls : 'roo-upload-cropbox-preview'
27137 cls : 'roo-upload-cropbox-thumb'
27141 cls : 'roo-upload-cropbox-empty-notify',
27142 html : this.emptyText
27146 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27147 html : this.rotateNotify
27153 cls : 'roo-upload-cropbox-footer',
27156 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27166 onRender : function(ct, position)
27168 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27170 if (this.buttons.length) {
27172 Roo.each(this.buttons, function(bb) {
27174 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27176 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27182 this.maskEl = this.el;
27186 initEvents : function()
27188 this.urlAPI = (window.createObjectURL && window) ||
27189 (window.URL && URL.revokeObjectURL && URL) ||
27190 (window.webkitURL && webkitURL);
27192 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27193 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27195 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27196 this.selectorEl.hide();
27198 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27199 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27201 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27202 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27203 this.thumbEl.hide();
27205 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27206 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27208 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27209 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27210 this.errorEl.hide();
27212 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27213 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27214 this.footerEl.hide();
27216 this.setThumbBoxSize();
27222 this.fireEvent('initial', this);
27229 window.addEventListener("resize", function() { _this.resize(); } );
27231 this.bodyEl.on('click', this.beforeSelectFile, this);
27234 this.bodyEl.on('touchstart', this.onTouchStart, this);
27235 this.bodyEl.on('touchmove', this.onTouchMove, this);
27236 this.bodyEl.on('touchend', this.onTouchEnd, this);
27240 this.bodyEl.on('mousedown', this.onMouseDown, this);
27241 this.bodyEl.on('mousemove', this.onMouseMove, this);
27242 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27243 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27244 Roo.get(document).on('mouseup', this.onMouseUp, this);
27247 this.selectorEl.on('change', this.onFileSelected, this);
27253 this.baseScale = 1;
27255 this.baseRotate = 1;
27256 this.dragable = false;
27257 this.pinching = false;
27260 this.cropData = false;
27261 this.notifyEl.dom.innerHTML = this.emptyText;
27263 this.selectorEl.dom.value = '';
27267 resize : function()
27269 if(this.fireEvent('resize', this) != false){
27270 this.setThumbBoxPosition();
27271 this.setCanvasPosition();
27275 onFooterButtonClick : function(e, el, o, type)
27278 case 'rotate-left' :
27279 this.onRotateLeft(e);
27281 case 'rotate-right' :
27282 this.onRotateRight(e);
27285 this.beforeSelectFile(e);
27300 this.fireEvent('footerbuttonclick', this, type);
27303 beforeSelectFile : function(e)
27305 e.preventDefault();
27307 if(this.fireEvent('beforeselectfile', this) != false){
27308 this.selectorEl.dom.click();
27312 onFileSelected : function(e)
27314 e.preventDefault();
27316 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27320 var file = this.selectorEl.dom.files[0];
27322 if(this.fireEvent('inspect', this, file) != false){
27323 this.prepare(file);
27328 trash : function(e)
27330 this.fireEvent('trash', this);
27333 download : function(e)
27335 this.fireEvent('download', this);
27338 loadCanvas : function(src)
27340 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27344 this.imageEl = document.createElement('img');
27348 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27350 this.imageEl.src = src;
27354 onLoadCanvas : function()
27356 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27357 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27359 this.bodyEl.un('click', this.beforeSelectFile, this);
27361 this.notifyEl.hide();
27362 this.thumbEl.show();
27363 this.footerEl.show();
27365 this.baseRotateLevel();
27367 if(this.isDocument){
27368 this.setThumbBoxSize();
27371 this.setThumbBoxPosition();
27373 this.baseScaleLevel();
27379 this.canvasLoaded = true;
27382 this.maskEl.unmask();
27387 setCanvasPosition : function()
27389 if(!this.canvasEl){
27393 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27394 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27396 this.previewEl.setLeft(pw);
27397 this.previewEl.setTop(ph);
27401 onMouseDown : function(e)
27405 this.dragable = true;
27406 this.pinching = false;
27408 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27409 this.dragable = false;
27413 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27414 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27418 onMouseMove : function(e)
27422 if(!this.canvasLoaded){
27426 if (!this.dragable){
27430 var minX = Math.ceil(this.thumbEl.getLeft(true));
27431 var minY = Math.ceil(this.thumbEl.getTop(true));
27433 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27434 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27436 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27437 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27439 x = x - this.mouseX;
27440 y = y - this.mouseY;
27442 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27443 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27445 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27446 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27448 this.previewEl.setLeft(bgX);
27449 this.previewEl.setTop(bgY);
27451 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27452 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27455 onMouseUp : function(e)
27459 this.dragable = false;
27462 onMouseWheel : function(e)
27466 this.startScale = this.scale;
27468 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27470 if(!this.zoomable()){
27471 this.scale = this.startScale;
27480 zoomable : function()
27482 var minScale = this.thumbEl.getWidth() / this.minWidth;
27484 if(this.minWidth < this.minHeight){
27485 minScale = this.thumbEl.getHeight() / this.minHeight;
27488 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27489 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27493 (this.rotate == 0 || this.rotate == 180) &&
27495 width > this.imageEl.OriginWidth ||
27496 height > this.imageEl.OriginHeight ||
27497 (width < this.minWidth && height < this.minHeight)
27505 (this.rotate == 90 || this.rotate == 270) &&
27507 width > this.imageEl.OriginWidth ||
27508 height > this.imageEl.OriginHeight ||
27509 (width < this.minHeight && height < this.minWidth)
27516 !this.isDocument &&
27517 (this.rotate == 0 || this.rotate == 180) &&
27519 width < this.minWidth ||
27520 width > this.imageEl.OriginWidth ||
27521 height < this.minHeight ||
27522 height > this.imageEl.OriginHeight
27529 !this.isDocument &&
27530 (this.rotate == 90 || this.rotate == 270) &&
27532 width < this.minHeight ||
27533 width > this.imageEl.OriginWidth ||
27534 height < this.minWidth ||
27535 height > this.imageEl.OriginHeight
27545 onRotateLeft : function(e)
27547 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27549 var minScale = this.thumbEl.getWidth() / this.minWidth;
27551 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27552 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27554 this.startScale = this.scale;
27556 while (this.getScaleLevel() < minScale){
27558 this.scale = this.scale + 1;
27560 if(!this.zoomable()){
27565 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27566 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27571 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27578 this.scale = this.startScale;
27580 this.onRotateFail();
27585 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27587 if(this.isDocument){
27588 this.setThumbBoxSize();
27589 this.setThumbBoxPosition();
27590 this.setCanvasPosition();
27595 this.fireEvent('rotate', this, 'left');
27599 onRotateRight : function(e)
27601 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27603 var minScale = this.thumbEl.getWidth() / this.minWidth;
27605 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27606 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27608 this.startScale = this.scale;
27610 while (this.getScaleLevel() < minScale){
27612 this.scale = this.scale + 1;
27614 if(!this.zoomable()){
27619 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27620 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27625 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27632 this.scale = this.startScale;
27634 this.onRotateFail();
27639 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27641 if(this.isDocument){
27642 this.setThumbBoxSize();
27643 this.setThumbBoxPosition();
27644 this.setCanvasPosition();
27649 this.fireEvent('rotate', this, 'right');
27652 onRotateFail : function()
27654 this.errorEl.show(true);
27658 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27663 this.previewEl.dom.innerHTML = '';
27665 var canvasEl = document.createElement("canvas");
27667 var contextEl = canvasEl.getContext("2d");
27669 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27670 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27671 var center = this.imageEl.OriginWidth / 2;
27673 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27674 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27675 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27676 center = this.imageEl.OriginHeight / 2;
27679 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27681 contextEl.translate(center, center);
27682 contextEl.rotate(this.rotate * Math.PI / 180);
27684 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27686 this.canvasEl = document.createElement("canvas");
27688 this.contextEl = this.canvasEl.getContext("2d");
27690 switch (this.rotate) {
27693 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27694 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27696 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27701 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27702 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27704 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27705 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);
27709 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27714 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27715 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27717 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27718 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);
27722 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);
27727 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27728 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27730 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27731 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27735 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);
27742 this.previewEl.appendChild(this.canvasEl);
27744 this.setCanvasPosition();
27749 if(!this.canvasLoaded){
27753 var imageCanvas = document.createElement("canvas");
27755 var imageContext = imageCanvas.getContext("2d");
27757 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27758 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27760 var center = imageCanvas.width / 2;
27762 imageContext.translate(center, center);
27764 imageContext.rotate(this.rotate * Math.PI / 180);
27766 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27768 var canvas = document.createElement("canvas");
27770 var context = canvas.getContext("2d");
27772 canvas.width = this.minWidth;
27773 canvas.height = this.minHeight;
27775 switch (this.rotate) {
27778 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27779 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27781 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27782 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27784 var targetWidth = this.minWidth - 2 * x;
27785 var targetHeight = this.minHeight - 2 * y;
27789 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27790 scale = targetWidth / width;
27793 if(x > 0 && y == 0){
27794 scale = targetHeight / height;
27797 if(x > 0 && y > 0){
27798 scale = targetWidth / width;
27800 if(width < height){
27801 scale = targetHeight / height;
27805 context.scale(scale, scale);
27807 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27808 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27810 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27811 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27813 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27818 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27819 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27821 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27822 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27824 var targetWidth = this.minWidth - 2 * x;
27825 var targetHeight = this.minHeight - 2 * y;
27829 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27830 scale = targetWidth / width;
27833 if(x > 0 && y == 0){
27834 scale = targetHeight / height;
27837 if(x > 0 && y > 0){
27838 scale = targetWidth / width;
27840 if(width < height){
27841 scale = targetHeight / height;
27845 context.scale(scale, scale);
27847 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27848 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27850 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27851 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27853 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27855 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27860 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27861 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27863 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27864 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27866 var targetWidth = this.minWidth - 2 * x;
27867 var targetHeight = this.minHeight - 2 * y;
27871 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27872 scale = targetWidth / width;
27875 if(x > 0 && y == 0){
27876 scale = targetHeight / height;
27879 if(x > 0 && y > 0){
27880 scale = targetWidth / width;
27882 if(width < height){
27883 scale = targetHeight / height;
27887 context.scale(scale, scale);
27889 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27890 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27892 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27893 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27895 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27896 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27898 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27903 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27904 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27906 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27907 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27909 var targetWidth = this.minWidth - 2 * x;
27910 var targetHeight = this.minHeight - 2 * y;
27914 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27915 scale = targetWidth / width;
27918 if(x > 0 && y == 0){
27919 scale = targetHeight / height;
27922 if(x > 0 && y > 0){
27923 scale = targetWidth / width;
27925 if(width < height){
27926 scale = targetHeight / height;
27930 context.scale(scale, scale);
27932 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27933 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27935 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27936 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27938 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27940 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27947 this.cropData = canvas.toDataURL(this.cropType);
27949 if(this.fireEvent('crop', this, this.cropData) !== false){
27950 this.process(this.file, this.cropData);
27957 setThumbBoxSize : function()
27961 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27962 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27963 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27965 this.minWidth = width;
27966 this.minHeight = height;
27968 if(this.rotate == 90 || this.rotate == 270){
27969 this.minWidth = height;
27970 this.minHeight = width;
27975 width = Math.ceil(this.minWidth * height / this.minHeight);
27977 if(this.minWidth > this.minHeight){
27979 height = Math.ceil(this.minHeight * width / this.minWidth);
27982 this.thumbEl.setStyle({
27983 width : width + 'px',
27984 height : height + 'px'
27991 setThumbBoxPosition : function()
27993 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27994 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27996 this.thumbEl.setLeft(x);
27997 this.thumbEl.setTop(y);
28001 baseRotateLevel : function()
28003 this.baseRotate = 1;
28006 typeof(this.exif) != 'undefined' &&
28007 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28008 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28010 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28013 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28017 baseScaleLevel : function()
28021 if(this.isDocument){
28023 if(this.baseRotate == 6 || this.baseRotate == 8){
28025 height = this.thumbEl.getHeight();
28026 this.baseScale = height / this.imageEl.OriginWidth;
28028 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28029 width = this.thumbEl.getWidth();
28030 this.baseScale = width / this.imageEl.OriginHeight;
28036 height = this.thumbEl.getHeight();
28037 this.baseScale = height / this.imageEl.OriginHeight;
28039 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28040 width = this.thumbEl.getWidth();
28041 this.baseScale = width / this.imageEl.OriginWidth;
28047 if(this.baseRotate == 6 || this.baseRotate == 8){
28049 width = this.thumbEl.getHeight();
28050 this.baseScale = width / this.imageEl.OriginHeight;
28052 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28053 height = this.thumbEl.getWidth();
28054 this.baseScale = height / this.imageEl.OriginHeight;
28057 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28058 height = this.thumbEl.getWidth();
28059 this.baseScale = height / this.imageEl.OriginHeight;
28061 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28062 width = this.thumbEl.getHeight();
28063 this.baseScale = width / this.imageEl.OriginWidth;
28070 width = this.thumbEl.getWidth();
28071 this.baseScale = width / this.imageEl.OriginWidth;
28073 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28074 height = this.thumbEl.getHeight();
28075 this.baseScale = height / this.imageEl.OriginHeight;
28078 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28080 height = this.thumbEl.getHeight();
28081 this.baseScale = height / this.imageEl.OriginHeight;
28083 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28084 width = this.thumbEl.getWidth();
28085 this.baseScale = width / this.imageEl.OriginWidth;
28093 getScaleLevel : function()
28095 return this.baseScale * Math.pow(1.1, this.scale);
28098 onTouchStart : function(e)
28100 if(!this.canvasLoaded){
28101 this.beforeSelectFile(e);
28105 var touches = e.browserEvent.touches;
28111 if(touches.length == 1){
28112 this.onMouseDown(e);
28116 if(touches.length != 2){
28122 for(var i = 0, finger; finger = touches[i]; i++){
28123 coords.push(finger.pageX, finger.pageY);
28126 var x = Math.pow(coords[0] - coords[2], 2);
28127 var y = Math.pow(coords[1] - coords[3], 2);
28129 this.startDistance = Math.sqrt(x + y);
28131 this.startScale = this.scale;
28133 this.pinching = true;
28134 this.dragable = false;
28138 onTouchMove : function(e)
28140 if(!this.pinching && !this.dragable){
28144 var touches = e.browserEvent.touches;
28151 this.onMouseMove(e);
28157 for(var i = 0, finger; finger = touches[i]; i++){
28158 coords.push(finger.pageX, finger.pageY);
28161 var x = Math.pow(coords[0] - coords[2], 2);
28162 var y = Math.pow(coords[1] - coords[3], 2);
28164 this.endDistance = Math.sqrt(x + y);
28166 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28168 if(!this.zoomable()){
28169 this.scale = this.startScale;
28177 onTouchEnd : function(e)
28179 this.pinching = false;
28180 this.dragable = false;
28184 process : function(file, crop)
28187 this.maskEl.mask(this.loadingText);
28190 this.xhr = new XMLHttpRequest();
28192 file.xhr = this.xhr;
28194 this.xhr.open(this.method, this.url, true);
28197 "Accept": "application/json",
28198 "Cache-Control": "no-cache",
28199 "X-Requested-With": "XMLHttpRequest"
28202 for (var headerName in headers) {
28203 var headerValue = headers[headerName];
28205 this.xhr.setRequestHeader(headerName, headerValue);
28211 this.xhr.onload = function()
28213 _this.xhrOnLoad(_this.xhr);
28216 this.xhr.onerror = function()
28218 _this.xhrOnError(_this.xhr);
28221 var formData = new FormData();
28223 formData.append('returnHTML', 'NO');
28226 formData.append('crop', crop);
28229 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28230 formData.append(this.paramName, file, file.name);
28233 if(typeof(file.filename) != 'undefined'){
28234 formData.append('filename', file.filename);
28237 if(typeof(file.mimetype) != 'undefined'){
28238 formData.append('mimetype', file.mimetype);
28241 if(this.fireEvent('arrange', this, formData) != false){
28242 this.xhr.send(formData);
28246 xhrOnLoad : function(xhr)
28249 this.maskEl.unmask();
28252 if (xhr.readyState !== 4) {
28253 this.fireEvent('exception', this, xhr);
28257 var response = Roo.decode(xhr.responseText);
28259 if(!response.success){
28260 this.fireEvent('exception', this, xhr);
28264 var response = Roo.decode(xhr.responseText);
28266 this.fireEvent('upload', this, response);
28270 xhrOnError : function()
28273 this.maskEl.unmask();
28276 Roo.log('xhr on error');
28278 var response = Roo.decode(xhr.responseText);
28284 prepare : function(file)
28287 this.maskEl.mask(this.loadingText);
28293 if(typeof(file) === 'string'){
28294 this.loadCanvas(file);
28298 if(!file || !this.urlAPI){
28303 this.cropType = file.type;
28307 if(this.fireEvent('prepare', this, this.file) != false){
28309 var reader = new FileReader();
28311 reader.onload = function (e) {
28312 if (e.target.error) {
28313 Roo.log(e.target.error);
28317 var buffer = e.target.result,
28318 dataView = new DataView(buffer),
28320 maxOffset = dataView.byteLength - 4,
28324 if (dataView.getUint16(0) === 0xffd8) {
28325 while (offset < maxOffset) {
28326 markerBytes = dataView.getUint16(offset);
28328 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28329 markerLength = dataView.getUint16(offset + 2) + 2;
28330 if (offset + markerLength > dataView.byteLength) {
28331 Roo.log('Invalid meta data: Invalid segment size.');
28335 if(markerBytes == 0xffe1){
28336 _this.parseExifData(
28343 offset += markerLength;
28353 var url = _this.urlAPI.createObjectURL(_this.file);
28355 _this.loadCanvas(url);
28360 reader.readAsArrayBuffer(this.file);
28366 parseExifData : function(dataView, offset, length)
28368 var tiffOffset = offset + 10,
28372 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28373 // No Exif data, might be XMP data instead
28377 // Check for the ASCII code for "Exif" (0x45786966):
28378 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28379 // No Exif data, might be XMP data instead
28382 if (tiffOffset + 8 > dataView.byteLength) {
28383 Roo.log('Invalid Exif data: Invalid segment size.');
28386 // Check for the two null bytes:
28387 if (dataView.getUint16(offset + 8) !== 0x0000) {
28388 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28391 // Check the byte alignment:
28392 switch (dataView.getUint16(tiffOffset)) {
28394 littleEndian = true;
28397 littleEndian = false;
28400 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28403 // Check for the TIFF tag marker (0x002A):
28404 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28405 Roo.log('Invalid Exif data: Missing TIFF marker.');
28408 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28409 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28411 this.parseExifTags(
28414 tiffOffset + dirOffset,
28419 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28424 if (dirOffset + 6 > dataView.byteLength) {
28425 Roo.log('Invalid Exif data: Invalid directory offset.');
28428 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28429 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28430 if (dirEndOffset + 4 > dataView.byteLength) {
28431 Roo.log('Invalid Exif data: Invalid directory size.');
28434 for (i = 0; i < tagsNumber; i += 1) {
28438 dirOffset + 2 + 12 * i, // tag offset
28442 // Return the offset to the next directory:
28443 return dataView.getUint32(dirEndOffset, littleEndian);
28446 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28448 var tag = dataView.getUint16(offset, littleEndian);
28450 this.exif[tag] = this.getExifValue(
28454 dataView.getUint16(offset + 2, littleEndian), // tag type
28455 dataView.getUint32(offset + 4, littleEndian), // tag length
28460 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28462 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28471 Roo.log('Invalid Exif data: Invalid tag type.');
28475 tagSize = tagType.size * length;
28476 // Determine if the value is contained in the dataOffset bytes,
28477 // or if the value at the dataOffset is a pointer to the actual data:
28478 dataOffset = tagSize > 4 ?
28479 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28480 if (dataOffset + tagSize > dataView.byteLength) {
28481 Roo.log('Invalid Exif data: Invalid data offset.');
28484 if (length === 1) {
28485 return tagType.getValue(dataView, dataOffset, littleEndian);
28488 for (i = 0; i < length; i += 1) {
28489 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28492 if (tagType.ascii) {
28494 // Concatenate the chars:
28495 for (i = 0; i < values.length; i += 1) {
28497 // Ignore the terminating NULL byte(s):
28498 if (c === '\u0000') {
28510 Roo.apply(Roo.bootstrap.UploadCropbox, {
28512 'Orientation': 0x0112
28516 1: 0, //'top-left',
28518 3: 180, //'bottom-right',
28519 // 4: 'bottom-left',
28521 6: 90, //'right-top',
28522 // 7: 'right-bottom',
28523 8: 270 //'left-bottom'
28527 // byte, 8-bit unsigned int:
28529 getValue: function (dataView, dataOffset) {
28530 return dataView.getUint8(dataOffset);
28534 // ascii, 8-bit byte:
28536 getValue: function (dataView, dataOffset) {
28537 return String.fromCharCode(dataView.getUint8(dataOffset));
28542 // short, 16 bit int:
28544 getValue: function (dataView, dataOffset, littleEndian) {
28545 return dataView.getUint16(dataOffset, littleEndian);
28549 // long, 32 bit int:
28551 getValue: function (dataView, dataOffset, littleEndian) {
28552 return dataView.getUint32(dataOffset, littleEndian);
28556 // rational = two long values, first is numerator, second is denominator:
28558 getValue: function (dataView, dataOffset, littleEndian) {
28559 return dataView.getUint32(dataOffset, littleEndian) /
28560 dataView.getUint32(dataOffset + 4, littleEndian);
28564 // slong, 32 bit signed int:
28566 getValue: function (dataView, dataOffset, littleEndian) {
28567 return dataView.getInt32(dataOffset, littleEndian);
28571 // srational, two slongs, first is numerator, second is denominator:
28573 getValue: function (dataView, dataOffset, littleEndian) {
28574 return dataView.getInt32(dataOffset, littleEndian) /
28575 dataView.getInt32(dataOffset + 4, littleEndian);
28585 cls : 'btn-group roo-upload-cropbox-rotate-left',
28586 action : 'rotate-left',
28590 cls : 'btn btn-default',
28591 html : '<i class="fa fa-undo"></i>'
28597 cls : 'btn-group roo-upload-cropbox-picture',
28598 action : 'picture',
28602 cls : 'btn btn-default',
28603 html : '<i class="fa fa-picture-o"></i>'
28609 cls : 'btn-group roo-upload-cropbox-rotate-right',
28610 action : 'rotate-right',
28614 cls : 'btn btn-default',
28615 html : '<i class="fa fa-repeat"></i>'
28623 cls : 'btn-group roo-upload-cropbox-rotate-left',
28624 action : 'rotate-left',
28628 cls : 'btn btn-default',
28629 html : '<i class="fa fa-undo"></i>'
28635 cls : 'btn-group roo-upload-cropbox-download',
28636 action : 'download',
28640 cls : 'btn btn-default',
28641 html : '<i class="fa fa-download"></i>'
28647 cls : 'btn-group roo-upload-cropbox-crop',
28652 cls : 'btn btn-default',
28653 html : '<i class="fa fa-crop"></i>'
28659 cls : 'btn-group roo-upload-cropbox-trash',
28664 cls : 'btn btn-default',
28665 html : '<i class="fa fa-trash"></i>'
28671 cls : 'btn-group roo-upload-cropbox-rotate-right',
28672 action : 'rotate-right',
28676 cls : 'btn btn-default',
28677 html : '<i class="fa fa-repeat"></i>'
28685 cls : 'btn-group roo-upload-cropbox-rotate-left',
28686 action : 'rotate-left',
28690 cls : 'btn btn-default',
28691 html : '<i class="fa fa-undo"></i>'
28697 cls : 'btn-group roo-upload-cropbox-rotate-right',
28698 action : 'rotate-right',
28702 cls : 'btn btn-default',
28703 html : '<i class="fa fa-repeat"></i>'
28716 * @class Roo.bootstrap.DocumentManager
28717 * @extends Roo.bootstrap.Component
28718 * Bootstrap DocumentManager class
28719 * @cfg {String} paramName default 'imageUpload'
28720 * @cfg {String} toolTipName default 'filename'
28721 * @cfg {String} method default POST
28722 * @cfg {String} url action url
28723 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28724 * @cfg {Boolean} multiple multiple upload default true
28725 * @cfg {Number} thumbSize default 300
28726 * @cfg {String} fieldLabel
28727 * @cfg {Number} labelWidth default 4
28728 * @cfg {String} labelAlign (left|top) default left
28729 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28730 * @cfg {Number} labellg set the width of label (1-12)
28731 * @cfg {Number} labelmd set the width of label (1-12)
28732 * @cfg {Number} labelsm set the width of label (1-12)
28733 * @cfg {Number} labelxs set the width of label (1-12)
28736 * Create a new DocumentManager
28737 * @param {Object} config The config object
28740 Roo.bootstrap.DocumentManager = function(config){
28741 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28744 this.delegates = [];
28749 * Fire when initial the DocumentManager
28750 * @param {Roo.bootstrap.DocumentManager} this
28755 * inspect selected file
28756 * @param {Roo.bootstrap.DocumentManager} this
28757 * @param {File} file
28762 * Fire when xhr load exception
28763 * @param {Roo.bootstrap.DocumentManager} this
28764 * @param {XMLHttpRequest} xhr
28766 "exception" : true,
28768 * @event afterupload
28769 * Fire when xhr load exception
28770 * @param {Roo.bootstrap.DocumentManager} this
28771 * @param {XMLHttpRequest} xhr
28773 "afterupload" : true,
28776 * prepare the form data
28777 * @param {Roo.bootstrap.DocumentManager} this
28778 * @param {Object} formData
28783 * Fire when remove the file
28784 * @param {Roo.bootstrap.DocumentManager} this
28785 * @param {Object} file
28790 * Fire after refresh the file
28791 * @param {Roo.bootstrap.DocumentManager} this
28796 * Fire after click the image
28797 * @param {Roo.bootstrap.DocumentManager} this
28798 * @param {Object} file
28803 * Fire when upload a image and editable set to true
28804 * @param {Roo.bootstrap.DocumentManager} this
28805 * @param {Object} file
28809 * @event beforeselectfile
28810 * Fire before select file
28811 * @param {Roo.bootstrap.DocumentManager} this
28813 "beforeselectfile" : true,
28816 * Fire before process file
28817 * @param {Roo.bootstrap.DocumentManager} this
28818 * @param {Object} file
28822 * @event previewrendered
28823 * Fire when preview rendered
28824 * @param {Roo.bootstrap.DocumentManager} this
28825 * @param {Object} file
28827 "previewrendered" : true,
28830 "previewResize" : true
28835 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28844 paramName : 'imageUpload',
28845 toolTipName : 'filename',
28848 labelAlign : 'left',
28858 getAutoCreate : function()
28860 var managerWidget = {
28862 cls : 'roo-document-manager',
28866 cls : 'roo-document-manager-selector',
28871 cls : 'roo-document-manager-uploader',
28875 cls : 'roo-document-manager-upload-btn',
28876 html : '<i class="fa fa-plus"></i>'
28887 cls : 'column col-md-12',
28892 if(this.fieldLabel.length){
28897 cls : 'column col-md-12',
28898 html : this.fieldLabel
28902 cls : 'column col-md-12',
28907 if(this.labelAlign == 'left'){
28912 html : this.fieldLabel
28921 if(this.labelWidth > 12){
28922 content[0].style = "width: " + this.labelWidth + 'px';
28925 if(this.labelWidth < 13 && this.labelmd == 0){
28926 this.labelmd = this.labelWidth;
28929 if(this.labellg > 0){
28930 content[0].cls += ' col-lg-' + this.labellg;
28931 content[1].cls += ' col-lg-' + (12 - this.labellg);
28934 if(this.labelmd > 0){
28935 content[0].cls += ' col-md-' + this.labelmd;
28936 content[1].cls += ' col-md-' + (12 - this.labelmd);
28939 if(this.labelsm > 0){
28940 content[0].cls += ' col-sm-' + this.labelsm;
28941 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28944 if(this.labelxs > 0){
28945 content[0].cls += ' col-xs-' + this.labelxs;
28946 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28954 cls : 'row clearfix',
28962 initEvents : function()
28964 this.managerEl = this.el.select('.roo-document-manager', true).first();
28965 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28967 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28968 this.selectorEl.hide();
28971 this.selectorEl.attr('multiple', 'multiple');
28974 this.selectorEl.on('change', this.onFileSelected, this);
28976 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28977 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28979 this.uploader.on('click', this.onUploaderClick, this);
28981 this.renderProgressDialog();
28985 window.addEventListener("resize", function() { _this.refresh(); } );
28987 this.fireEvent('initial', this);
28990 renderProgressDialog : function()
28994 this.progressDialog = new Roo.bootstrap.Modal({
28995 cls : 'roo-document-manager-progress-dialog',
28996 allow_close : false,
29006 btnclick : function() {
29007 _this.uploadCancel();
29013 this.progressDialog.render(Roo.get(document.body));
29015 this.progress = new Roo.bootstrap.Progress({
29016 cls : 'roo-document-manager-progress',
29021 this.progress.render(this.progressDialog.getChildContainer());
29023 this.progressBar = new Roo.bootstrap.ProgressBar({
29024 cls : 'roo-document-manager-progress-bar',
29027 aria_valuemax : 12,
29031 this.progressBar.render(this.progress.getChildContainer());
29034 onUploaderClick : function(e)
29036 e.preventDefault();
29038 if(this.fireEvent('beforeselectfile', this) != false){
29039 this.selectorEl.dom.click();
29044 onFileSelected : function(e)
29046 e.preventDefault();
29048 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29052 Roo.each(this.selectorEl.dom.files, function(file){
29053 if(this.fireEvent('inspect', this, file) != false){
29054 this.files.push(file);
29064 this.selectorEl.dom.value = '';
29066 if(!this.files || !this.files.length){
29070 if(this.boxes > 0 && this.files.length > this.boxes){
29071 this.files = this.files.slice(0, this.boxes);
29074 this.uploader.show();
29076 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29077 this.uploader.hide();
29086 Roo.each(this.files, function(file){
29088 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29089 var f = this.renderPreview(file);
29094 if(file.type.indexOf('image') != -1){
29095 this.delegates.push(
29097 _this.process(file);
29098 }).createDelegate(this)
29106 _this.process(file);
29107 }).createDelegate(this)
29112 this.files = files;
29114 this.delegates = this.delegates.concat(docs);
29116 if(!this.delegates.length){
29121 this.progressBar.aria_valuemax = this.delegates.length;
29128 arrange : function()
29130 if(!this.delegates.length){
29131 this.progressDialog.hide();
29136 var delegate = this.delegates.shift();
29138 this.progressDialog.show();
29140 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29142 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29147 refresh : function()
29149 this.uploader.show();
29151 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29152 this.uploader.hide();
29155 Roo.isTouch ? this.closable(false) : this.closable(true);
29157 this.fireEvent('refresh', this);
29160 onRemove : function(e, el, o)
29162 e.preventDefault();
29164 this.fireEvent('remove', this, o);
29168 remove : function(o)
29172 Roo.each(this.files, function(file){
29173 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29182 this.files = files;
29189 Roo.each(this.files, function(file){
29194 file.target.remove();
29203 onClick : function(e, el, o)
29205 e.preventDefault();
29207 this.fireEvent('click', this, o);
29211 closable : function(closable)
29213 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29215 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29227 xhrOnLoad : function(xhr)
29229 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29233 if (xhr.readyState !== 4) {
29235 this.fireEvent('exception', this, xhr);
29239 var response = Roo.decode(xhr.responseText);
29241 if(!response.success){
29243 this.fireEvent('exception', this, xhr);
29247 var file = this.renderPreview(response.data);
29249 this.files.push(file);
29253 this.fireEvent('afterupload', this, xhr);
29257 xhrOnError : function(xhr)
29259 Roo.log('xhr on error');
29261 var response = Roo.decode(xhr.responseText);
29268 process : function(file)
29270 if(this.fireEvent('process', this, file) !== false){
29271 if(this.editable && file.type.indexOf('image') != -1){
29272 this.fireEvent('edit', this, file);
29276 this.uploadStart(file, false);
29283 uploadStart : function(file, crop)
29285 this.xhr = new XMLHttpRequest();
29287 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29292 file.xhr = this.xhr;
29294 this.managerEl.createChild({
29296 cls : 'roo-document-manager-loading',
29300 tooltip : file.name,
29301 cls : 'roo-document-manager-thumb',
29302 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29308 this.xhr.open(this.method, this.url, true);
29311 "Accept": "application/json",
29312 "Cache-Control": "no-cache",
29313 "X-Requested-With": "XMLHttpRequest"
29316 for (var headerName in headers) {
29317 var headerValue = headers[headerName];
29319 this.xhr.setRequestHeader(headerName, headerValue);
29325 this.xhr.onload = function()
29327 _this.xhrOnLoad(_this.xhr);
29330 this.xhr.onerror = function()
29332 _this.xhrOnError(_this.xhr);
29335 var formData = new FormData();
29337 formData.append('returnHTML', 'NO');
29340 formData.append('crop', crop);
29343 formData.append(this.paramName, file, file.name);
29350 if(this.fireEvent('prepare', this, formData, options) != false){
29352 if(options.manually){
29356 this.xhr.send(formData);
29360 this.uploadCancel();
29363 uploadCancel : function()
29369 this.delegates = [];
29371 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29378 renderPreview : function(file)
29380 if(typeof(file.target) != 'undefined' && file.target){
29384 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29386 var previewEl = this.managerEl.createChild({
29388 cls : 'roo-document-manager-preview',
29392 tooltip : file[this.toolTipName],
29393 cls : 'roo-document-manager-thumb',
29394 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29399 html : '<i class="fa fa-times-circle"></i>'
29404 var close = previewEl.select('button.close', true).first();
29406 close.on('click', this.onRemove, this, file);
29408 file.target = previewEl;
29410 var image = previewEl.select('img', true).first();
29414 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29416 image.on('click', this.onClick, this, file);
29418 this.fireEvent('previewrendered', this, file);
29424 onPreviewLoad : function(file, image)
29426 if(typeof(file.target) == 'undefined' || !file.target){
29430 var width = image.dom.naturalWidth || image.dom.width;
29431 var height = image.dom.naturalHeight || image.dom.height;
29433 if(!this.previewResize) {
29437 if(width > height){
29438 file.target.addClass('wide');
29442 file.target.addClass('tall');
29447 uploadFromSource : function(file, crop)
29449 this.xhr = new XMLHttpRequest();
29451 this.managerEl.createChild({
29453 cls : 'roo-document-manager-loading',
29457 tooltip : file.name,
29458 cls : 'roo-document-manager-thumb',
29459 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29465 this.xhr.open(this.method, this.url, true);
29468 "Accept": "application/json",
29469 "Cache-Control": "no-cache",
29470 "X-Requested-With": "XMLHttpRequest"
29473 for (var headerName in headers) {
29474 var headerValue = headers[headerName];
29476 this.xhr.setRequestHeader(headerName, headerValue);
29482 this.xhr.onload = function()
29484 _this.xhrOnLoad(_this.xhr);
29487 this.xhr.onerror = function()
29489 _this.xhrOnError(_this.xhr);
29492 var formData = new FormData();
29494 formData.append('returnHTML', 'NO');
29496 formData.append('crop', crop);
29498 if(typeof(file.filename) != 'undefined'){
29499 formData.append('filename', file.filename);
29502 if(typeof(file.mimetype) != 'undefined'){
29503 formData.append('mimetype', file.mimetype);
29508 if(this.fireEvent('prepare', this, formData) != false){
29509 this.xhr.send(formData);
29519 * @class Roo.bootstrap.DocumentViewer
29520 * @extends Roo.bootstrap.Component
29521 * Bootstrap DocumentViewer class
29522 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29523 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29526 * Create a new DocumentViewer
29527 * @param {Object} config The config object
29530 Roo.bootstrap.DocumentViewer = function(config){
29531 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29536 * Fire after initEvent
29537 * @param {Roo.bootstrap.DocumentViewer} this
29543 * @param {Roo.bootstrap.DocumentViewer} this
29548 * Fire after download button
29549 * @param {Roo.bootstrap.DocumentViewer} this
29554 * Fire after trash button
29555 * @param {Roo.bootstrap.DocumentViewer} this
29562 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29564 showDownload : true,
29568 getAutoCreate : function()
29572 cls : 'roo-document-viewer',
29576 cls : 'roo-document-viewer-body',
29580 cls : 'roo-document-viewer-thumb',
29584 cls : 'roo-document-viewer-image'
29592 cls : 'roo-document-viewer-footer',
29595 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29599 cls : 'btn-group roo-document-viewer-download',
29603 cls : 'btn btn-default',
29604 html : '<i class="fa fa-download"></i>'
29610 cls : 'btn-group roo-document-viewer-trash',
29614 cls : 'btn btn-default',
29615 html : '<i class="fa fa-trash"></i>'
29628 initEvents : function()
29630 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29631 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29633 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29634 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29636 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29637 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29639 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29640 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29642 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29643 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29645 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29646 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29648 this.bodyEl.on('click', this.onClick, this);
29649 this.downloadBtn.on('click', this.onDownload, this);
29650 this.trashBtn.on('click', this.onTrash, this);
29652 this.downloadBtn.hide();
29653 this.trashBtn.hide();
29655 if(this.showDownload){
29656 this.downloadBtn.show();
29659 if(this.showTrash){
29660 this.trashBtn.show();
29663 if(!this.showDownload && !this.showTrash) {
29664 this.footerEl.hide();
29669 initial : function()
29671 this.fireEvent('initial', this);
29675 onClick : function(e)
29677 e.preventDefault();
29679 this.fireEvent('click', this);
29682 onDownload : function(e)
29684 e.preventDefault();
29686 this.fireEvent('download', this);
29689 onTrash : function(e)
29691 e.preventDefault();
29693 this.fireEvent('trash', this);
29705 * @class Roo.bootstrap.NavProgressBar
29706 * @extends Roo.bootstrap.Component
29707 * Bootstrap NavProgressBar class
29710 * Create a new nav progress bar
29711 * @param {Object} config The config object
29714 Roo.bootstrap.NavProgressBar = function(config){
29715 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29717 this.bullets = this.bullets || [];
29719 // Roo.bootstrap.NavProgressBar.register(this);
29723 * Fires when the active item changes
29724 * @param {Roo.bootstrap.NavProgressBar} this
29725 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29726 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29733 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29738 getAutoCreate : function()
29740 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29744 cls : 'roo-navigation-bar-group',
29748 cls : 'roo-navigation-top-bar'
29752 cls : 'roo-navigation-bullets-bar',
29756 cls : 'roo-navigation-bar'
29763 cls : 'roo-navigation-bottom-bar'
29773 initEvents: function()
29778 onRender : function(ct, position)
29780 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29782 if(this.bullets.length){
29783 Roo.each(this.bullets, function(b){
29792 addItem : function(cfg)
29794 var item = new Roo.bootstrap.NavProgressItem(cfg);
29796 item.parentId = this.id;
29797 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29800 var top = new Roo.bootstrap.Element({
29802 cls : 'roo-navigation-bar-text'
29805 var bottom = new Roo.bootstrap.Element({
29807 cls : 'roo-navigation-bar-text'
29810 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29811 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29813 var topText = new Roo.bootstrap.Element({
29815 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29818 var bottomText = new Roo.bootstrap.Element({
29820 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29823 topText.onRender(top.el, null);
29824 bottomText.onRender(bottom.el, null);
29827 item.bottomEl = bottom;
29830 this.barItems.push(item);
29835 getActive : function()
29837 var active = false;
29839 Roo.each(this.barItems, function(v){
29841 if (!v.isActive()) {
29853 setActiveItem : function(item)
29857 Roo.each(this.barItems, function(v){
29858 if (v.rid == item.rid) {
29862 if (v.isActive()) {
29863 v.setActive(false);
29868 item.setActive(true);
29870 this.fireEvent('changed', this, item, prev);
29873 getBarItem: function(rid)
29877 Roo.each(this.barItems, function(e) {
29878 if (e.rid != rid) {
29889 indexOfItem : function(item)
29893 Roo.each(this.barItems, function(v, i){
29895 if (v.rid != item.rid) {
29906 setActiveNext : function()
29908 var i = this.indexOfItem(this.getActive());
29910 if (i > this.barItems.length) {
29914 this.setActiveItem(this.barItems[i+1]);
29917 setActivePrev : function()
29919 var i = this.indexOfItem(this.getActive());
29925 this.setActiveItem(this.barItems[i-1]);
29928 format : function()
29930 if(!this.barItems.length){
29934 var width = 100 / this.barItems.length;
29936 Roo.each(this.barItems, function(i){
29937 i.el.setStyle('width', width + '%');
29938 i.topEl.el.setStyle('width', width + '%');
29939 i.bottomEl.el.setStyle('width', width + '%');
29948 * Nav Progress Item
29953 * @class Roo.bootstrap.NavProgressItem
29954 * @extends Roo.bootstrap.Component
29955 * Bootstrap NavProgressItem class
29956 * @cfg {String} rid the reference id
29957 * @cfg {Boolean} active (true|false) Is item active default false
29958 * @cfg {Boolean} disabled (true|false) Is item active default false
29959 * @cfg {String} html
29960 * @cfg {String} position (top|bottom) text position default bottom
29961 * @cfg {String} icon show icon instead of number
29964 * Create a new NavProgressItem
29965 * @param {Object} config The config object
29967 Roo.bootstrap.NavProgressItem = function(config){
29968 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29973 * The raw click event for the entire grid.
29974 * @param {Roo.bootstrap.NavProgressItem} this
29975 * @param {Roo.EventObject} e
29982 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29988 position : 'bottom',
29991 getAutoCreate : function()
29993 var iconCls = 'roo-navigation-bar-item-icon';
29995 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29999 cls: 'roo-navigation-bar-item',
30009 cfg.cls += ' active';
30012 cfg.cls += ' disabled';
30018 disable : function()
30020 this.setDisabled(true);
30023 enable : function()
30025 this.setDisabled(false);
30028 initEvents: function()
30030 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30032 this.iconEl.on('click', this.onClick, this);
30035 onClick : function(e)
30037 e.preventDefault();
30043 if(this.fireEvent('click', this, e) === false){
30047 this.parent().setActiveItem(this);
30050 isActive: function ()
30052 return this.active;
30055 setActive : function(state)
30057 if(this.active == state){
30061 this.active = state;
30064 this.el.addClass('active');
30068 this.el.removeClass('active');
30073 setDisabled : function(state)
30075 if(this.disabled == state){
30079 this.disabled = state;
30082 this.el.addClass('disabled');
30086 this.el.removeClass('disabled');
30089 tooltipEl : function()
30091 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30104 * @class Roo.bootstrap.FieldLabel
30105 * @extends Roo.bootstrap.Component
30106 * Bootstrap FieldLabel class
30107 * @cfg {String} html contents of the element
30108 * @cfg {String} tag tag of the element default label
30109 * @cfg {String} cls class of the element
30110 * @cfg {String} target label target
30111 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30112 * @cfg {String} invalidClass default "text-warning"
30113 * @cfg {String} validClass default "text-success"
30114 * @cfg {String} iconTooltip default "This field is required"
30115 * @cfg {String} indicatorpos (left|right) default left
30118 * Create a new FieldLabel
30119 * @param {Object} config The config object
30122 Roo.bootstrap.FieldLabel = function(config){
30123 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30128 * Fires after the field has been marked as invalid.
30129 * @param {Roo.form.FieldLabel} this
30130 * @param {String} msg The validation message
30135 * Fires after the field has been validated with no errors.
30136 * @param {Roo.form.FieldLabel} this
30142 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30149 invalidClass : 'has-warning',
30150 validClass : 'has-success',
30151 iconTooltip : 'This field is required',
30152 indicatorpos : 'left',
30154 getAutoCreate : function(){
30157 if (!this.allowBlank) {
30163 cls : 'roo-bootstrap-field-label ' + this.cls,
30168 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30169 tooltip : this.iconTooltip
30178 if(this.indicatorpos == 'right'){
30181 cls : 'roo-bootstrap-field-label ' + this.cls,
30190 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30191 tooltip : this.iconTooltip
30200 initEvents: function()
30202 Roo.bootstrap.Element.superclass.initEvents.call(this);
30204 this.indicator = this.indicatorEl();
30206 if(this.indicator){
30207 this.indicator.removeClass('visible');
30208 this.indicator.addClass('invisible');
30211 Roo.bootstrap.FieldLabel.register(this);
30214 indicatorEl : function()
30216 var indicator = this.el.select('i.roo-required-indicator',true).first();
30227 * Mark this field as valid
30229 markValid : function()
30231 if(this.indicator){
30232 this.indicator.removeClass('visible');
30233 this.indicator.addClass('invisible');
30236 this.el.removeClass(this.invalidClass);
30238 this.el.addClass(this.validClass);
30240 this.fireEvent('valid', this);
30244 * Mark this field as invalid
30245 * @param {String} msg The validation message
30247 markInvalid : function(msg)
30249 if(this.indicator){
30250 this.indicator.removeClass('invisible');
30251 this.indicator.addClass('visible');
30254 this.el.removeClass(this.validClass);
30256 this.el.addClass(this.invalidClass);
30258 this.fireEvent('invalid', this, msg);
30264 Roo.apply(Roo.bootstrap.FieldLabel, {
30269 * register a FieldLabel Group
30270 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30272 register : function(label)
30274 if(this.groups.hasOwnProperty(label.target)){
30278 this.groups[label.target] = label;
30282 * fetch a FieldLabel Group based on the target
30283 * @param {string} target
30284 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30286 get: function(target) {
30287 if (typeof(this.groups[target]) == 'undefined') {
30291 return this.groups[target] ;
30300 * page DateSplitField.
30306 * @class Roo.bootstrap.DateSplitField
30307 * @extends Roo.bootstrap.Component
30308 * Bootstrap DateSplitField class
30309 * @cfg {string} fieldLabel - the label associated
30310 * @cfg {Number} labelWidth set the width of label (0-12)
30311 * @cfg {String} labelAlign (top|left)
30312 * @cfg {Boolean} dayAllowBlank (true|false) default false
30313 * @cfg {Boolean} monthAllowBlank (true|false) default false
30314 * @cfg {Boolean} yearAllowBlank (true|false) default false
30315 * @cfg {string} dayPlaceholder
30316 * @cfg {string} monthPlaceholder
30317 * @cfg {string} yearPlaceholder
30318 * @cfg {string} dayFormat default 'd'
30319 * @cfg {string} monthFormat default 'm'
30320 * @cfg {string} yearFormat default 'Y'
30321 * @cfg {Number} labellg set the width of label (1-12)
30322 * @cfg {Number} labelmd set the width of label (1-12)
30323 * @cfg {Number} labelsm set the width of label (1-12)
30324 * @cfg {Number} labelxs set the width of label (1-12)
30328 * Create a new DateSplitField
30329 * @param {Object} config The config object
30332 Roo.bootstrap.DateSplitField = function(config){
30333 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30339 * getting the data of years
30340 * @param {Roo.bootstrap.DateSplitField} this
30341 * @param {Object} years
30346 * getting the data of days
30347 * @param {Roo.bootstrap.DateSplitField} this
30348 * @param {Object} days
30353 * Fires after the field has been marked as invalid.
30354 * @param {Roo.form.Field} this
30355 * @param {String} msg The validation message
30360 * Fires after the field has been validated with no errors.
30361 * @param {Roo.form.Field} this
30367 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30370 labelAlign : 'top',
30372 dayAllowBlank : false,
30373 monthAllowBlank : false,
30374 yearAllowBlank : false,
30375 dayPlaceholder : '',
30376 monthPlaceholder : '',
30377 yearPlaceholder : '',
30381 isFormField : true,
30387 getAutoCreate : function()
30391 cls : 'row roo-date-split-field-group',
30396 cls : 'form-hidden-field roo-date-split-field-group-value',
30402 var labelCls = 'col-md-12';
30403 var contentCls = 'col-md-4';
30405 if(this.fieldLabel){
30409 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30413 html : this.fieldLabel
30418 if(this.labelAlign == 'left'){
30420 if(this.labelWidth > 12){
30421 label.style = "width: " + this.labelWidth + 'px';
30424 if(this.labelWidth < 13 && this.labelmd == 0){
30425 this.labelmd = this.labelWidth;
30428 if(this.labellg > 0){
30429 labelCls = ' col-lg-' + this.labellg;
30430 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30433 if(this.labelmd > 0){
30434 labelCls = ' col-md-' + this.labelmd;
30435 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30438 if(this.labelsm > 0){
30439 labelCls = ' col-sm-' + this.labelsm;
30440 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30443 if(this.labelxs > 0){
30444 labelCls = ' col-xs-' + this.labelxs;
30445 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30449 label.cls += ' ' + labelCls;
30451 cfg.cn.push(label);
30454 Roo.each(['day', 'month', 'year'], function(t){
30457 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30464 inputEl: function ()
30466 return this.el.select('.roo-date-split-field-group-value', true).first();
30469 onRender : function(ct, position)
30473 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30475 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30477 this.dayField = new Roo.bootstrap.ComboBox({
30478 allowBlank : this.dayAllowBlank,
30479 alwaysQuery : true,
30480 displayField : 'value',
30483 forceSelection : true,
30485 placeholder : this.dayPlaceholder,
30486 selectOnFocus : true,
30487 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30488 triggerAction : 'all',
30490 valueField : 'value',
30491 store : new Roo.data.SimpleStore({
30492 data : (function() {
30494 _this.fireEvent('days', _this, days);
30497 fields : [ 'value' ]
30500 select : function (_self, record, index)
30502 _this.setValue(_this.getValue());
30507 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30509 this.monthField = new Roo.bootstrap.MonthField({
30510 after : '<i class=\"fa fa-calendar\"></i>',
30511 allowBlank : this.monthAllowBlank,
30512 placeholder : this.monthPlaceholder,
30515 render : function (_self)
30517 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30518 e.preventDefault();
30522 select : function (_self, oldvalue, newvalue)
30524 _this.setValue(_this.getValue());
30529 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30531 this.yearField = new Roo.bootstrap.ComboBox({
30532 allowBlank : this.yearAllowBlank,
30533 alwaysQuery : true,
30534 displayField : 'value',
30537 forceSelection : true,
30539 placeholder : this.yearPlaceholder,
30540 selectOnFocus : true,
30541 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30542 triggerAction : 'all',
30544 valueField : 'value',
30545 store : new Roo.data.SimpleStore({
30546 data : (function() {
30548 _this.fireEvent('years', _this, years);
30551 fields : [ 'value' ]
30554 select : function (_self, record, index)
30556 _this.setValue(_this.getValue());
30561 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30564 setValue : function(v, format)
30566 this.inputEl.dom.value = v;
30568 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30570 var d = Date.parseDate(v, f);
30577 this.setDay(d.format(this.dayFormat));
30578 this.setMonth(d.format(this.monthFormat));
30579 this.setYear(d.format(this.yearFormat));
30586 setDay : function(v)
30588 this.dayField.setValue(v);
30589 this.inputEl.dom.value = this.getValue();
30594 setMonth : function(v)
30596 this.monthField.setValue(v, true);
30597 this.inputEl.dom.value = this.getValue();
30602 setYear : function(v)
30604 this.yearField.setValue(v);
30605 this.inputEl.dom.value = this.getValue();
30610 getDay : function()
30612 return this.dayField.getValue();
30615 getMonth : function()
30617 return this.monthField.getValue();
30620 getYear : function()
30622 return this.yearField.getValue();
30625 getValue : function()
30627 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30629 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30639 this.inputEl.dom.value = '';
30644 validate : function()
30646 var d = this.dayField.validate();
30647 var m = this.monthField.validate();
30648 var y = this.yearField.validate();
30653 (!this.dayAllowBlank && !d) ||
30654 (!this.monthAllowBlank && !m) ||
30655 (!this.yearAllowBlank && !y)
30660 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30669 this.markInvalid();
30674 markValid : function()
30677 var label = this.el.select('label', true).first();
30678 var icon = this.el.select('i.fa-star', true).first();
30684 this.fireEvent('valid', this);
30688 * Mark this field as invalid
30689 * @param {String} msg The validation message
30691 markInvalid : function(msg)
30694 var label = this.el.select('label', true).first();
30695 var icon = this.el.select('i.fa-star', true).first();
30697 if(label && !icon){
30698 this.el.select('.roo-date-split-field-label', true).createChild({
30700 cls : 'text-danger fa fa-lg fa-star',
30701 tooltip : 'This field is required',
30702 style : 'margin-right:5px;'
30706 this.fireEvent('invalid', this, msg);
30709 clearInvalid : function()
30711 var label = this.el.select('label', true).first();
30712 var icon = this.el.select('i.fa-star', true).first();
30718 this.fireEvent('valid', this);
30721 getName: function()
30731 * http://masonry.desandro.com
30733 * The idea is to render all the bricks based on vertical width...
30735 * The original code extends 'outlayer' - we might need to use that....
30741 * @class Roo.bootstrap.LayoutMasonry
30742 * @extends Roo.bootstrap.Component
30743 * Bootstrap Layout Masonry class
30746 * Create a new Element
30747 * @param {Object} config The config object
30750 Roo.bootstrap.LayoutMasonry = function(config){
30752 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30756 Roo.bootstrap.LayoutMasonry.register(this);
30762 * Fire after layout the items
30763 * @param {Roo.bootstrap.LayoutMasonry} this
30764 * @param {Roo.EventObject} e
30771 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30774 * @cfg {Boolean} isLayoutInstant = no animation?
30776 isLayoutInstant : false, // needed?
30779 * @cfg {Number} boxWidth width of the columns
30784 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30789 * @cfg {Number} padWidth padding below box..
30794 * @cfg {Number} gutter gutter width..
30799 * @cfg {Number} maxCols maximum number of columns
30805 * @cfg {Boolean} isAutoInitial defalut true
30807 isAutoInitial : true,
30812 * @cfg {Boolean} isHorizontal defalut false
30814 isHorizontal : false,
30816 currentSize : null,
30822 bricks: null, //CompositeElement
30826 _isLayoutInited : false,
30828 // isAlternative : false, // only use for vertical layout...
30831 * @cfg {Number} alternativePadWidth padding below box..
30833 alternativePadWidth : 50,
30835 selectedBrick : [],
30837 getAutoCreate : function(){
30839 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30843 cls: 'blog-masonary-wrapper ' + this.cls,
30845 cls : 'mas-boxes masonary'
30852 getChildContainer: function( )
30854 if (this.boxesEl) {
30855 return this.boxesEl;
30858 this.boxesEl = this.el.select('.mas-boxes').first();
30860 return this.boxesEl;
30864 initEvents : function()
30868 if(this.isAutoInitial){
30869 Roo.log('hook children rendered');
30870 this.on('childrenrendered', function() {
30871 Roo.log('children rendered');
30877 initial : function()
30879 this.selectedBrick = [];
30881 this.currentSize = this.el.getBox(true);
30883 Roo.EventManager.onWindowResize(this.resize, this);
30885 if(!this.isAutoInitial){
30893 //this.layout.defer(500,this);
30897 resize : function()
30899 var cs = this.el.getBox(true);
30902 this.currentSize.width == cs.width &&
30903 this.currentSize.x == cs.x &&
30904 this.currentSize.height == cs.height &&
30905 this.currentSize.y == cs.y
30907 Roo.log("no change in with or X or Y");
30911 this.currentSize = cs;
30917 layout : function()
30919 this._resetLayout();
30921 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30923 this.layoutItems( isInstant );
30925 this._isLayoutInited = true;
30927 this.fireEvent('layout', this);
30931 _resetLayout : function()
30933 if(this.isHorizontal){
30934 this.horizontalMeasureColumns();
30938 this.verticalMeasureColumns();
30942 verticalMeasureColumns : function()
30944 this.getContainerWidth();
30946 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30947 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30951 var boxWidth = this.boxWidth + this.padWidth;
30953 if(this.containerWidth < this.boxWidth){
30954 boxWidth = this.containerWidth
30957 var containerWidth = this.containerWidth;
30959 var cols = Math.floor(containerWidth / boxWidth);
30961 this.cols = Math.max( cols, 1 );
30963 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30965 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30967 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30969 this.colWidth = boxWidth + avail - this.padWidth;
30971 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30972 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30975 horizontalMeasureColumns : function()
30977 this.getContainerWidth();
30979 var boxWidth = this.boxWidth;
30981 if(this.containerWidth < boxWidth){
30982 boxWidth = this.containerWidth;
30985 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30987 this.el.setHeight(boxWidth);
30991 getContainerWidth : function()
30993 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30996 layoutItems : function( isInstant )
30998 Roo.log(this.bricks);
31000 var items = Roo.apply([], this.bricks);
31002 if(this.isHorizontal){
31003 this._horizontalLayoutItems( items , isInstant );
31007 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31008 // this._verticalAlternativeLayoutItems( items , isInstant );
31012 this._verticalLayoutItems( items , isInstant );
31016 _verticalLayoutItems : function ( items , isInstant)
31018 if ( !items || !items.length ) {
31023 ['xs', 'xs', 'xs', 'tall'],
31024 ['xs', 'xs', 'tall'],
31025 ['xs', 'xs', 'sm'],
31026 ['xs', 'xs', 'xs'],
31032 ['sm', 'xs', 'xs'],
31036 ['tall', 'xs', 'xs', 'xs'],
31037 ['tall', 'xs', 'xs'],
31049 Roo.each(items, function(item, k){
31051 switch (item.size) {
31052 // these layouts take up a full box,
31063 boxes.push([item]);
31086 var filterPattern = function(box, length)
31094 var pattern = box.slice(0, length);
31098 Roo.each(pattern, function(i){
31099 format.push(i.size);
31102 Roo.each(standard, function(s){
31104 if(String(s) != String(format)){
31113 if(!match && length == 1){
31118 filterPattern(box, length - 1);
31122 queue.push(pattern);
31124 box = box.slice(length, box.length);
31126 filterPattern(box, 4);
31132 Roo.each(boxes, function(box, k){
31138 if(box.length == 1){
31143 filterPattern(box, 4);
31147 this._processVerticalLayoutQueue( queue, isInstant );
31151 // _verticalAlternativeLayoutItems : function( items , isInstant )
31153 // if ( !items || !items.length ) {
31157 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31161 _horizontalLayoutItems : function ( items , isInstant)
31163 if ( !items || !items.length || items.length < 3) {
31169 var eItems = items.slice(0, 3);
31171 items = items.slice(3, items.length);
31174 ['xs', 'xs', 'xs', 'wide'],
31175 ['xs', 'xs', 'wide'],
31176 ['xs', 'xs', 'sm'],
31177 ['xs', 'xs', 'xs'],
31183 ['sm', 'xs', 'xs'],
31187 ['wide', 'xs', 'xs', 'xs'],
31188 ['wide', 'xs', 'xs'],
31201 Roo.each(items, function(item, k){
31203 switch (item.size) {
31214 boxes.push([item]);
31238 var filterPattern = function(box, length)
31246 var pattern = box.slice(0, length);
31250 Roo.each(pattern, function(i){
31251 format.push(i.size);
31254 Roo.each(standard, function(s){
31256 if(String(s) != String(format)){
31265 if(!match && length == 1){
31270 filterPattern(box, length - 1);
31274 queue.push(pattern);
31276 box = box.slice(length, box.length);
31278 filterPattern(box, 4);
31284 Roo.each(boxes, function(box, k){
31290 if(box.length == 1){
31295 filterPattern(box, 4);
31302 var pos = this.el.getBox(true);
31306 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31308 var hit_end = false;
31310 Roo.each(queue, function(box){
31314 Roo.each(box, function(b){
31316 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31326 Roo.each(box, function(b){
31328 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31331 mx = Math.max(mx, b.x);
31335 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31339 Roo.each(box, function(b){
31341 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31355 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31358 /** Sets position of item in DOM
31359 * @param {Element} item
31360 * @param {Number} x - horizontal position
31361 * @param {Number} y - vertical position
31362 * @param {Boolean} isInstant - disables transitions
31364 _processVerticalLayoutQueue : function( queue, isInstant )
31366 var pos = this.el.getBox(true);
31371 for (var i = 0; i < this.cols; i++){
31375 Roo.each(queue, function(box, k){
31377 var col = k % this.cols;
31379 Roo.each(box, function(b,kk){
31381 b.el.position('absolute');
31383 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31384 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31386 if(b.size == 'md-left' || b.size == 'md-right'){
31387 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31388 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31391 b.el.setWidth(width);
31392 b.el.setHeight(height);
31394 b.el.select('iframe',true).setSize(width,height);
31398 for (var i = 0; i < this.cols; i++){
31400 if(maxY[i] < maxY[col]){
31405 col = Math.min(col, i);
31409 x = pos.x + col * (this.colWidth + this.padWidth);
31413 var positions = [];
31415 switch (box.length){
31417 positions = this.getVerticalOneBoxColPositions(x, y, box);
31420 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31423 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31426 positions = this.getVerticalFourBoxColPositions(x, y, box);
31432 Roo.each(box, function(b,kk){
31434 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31436 var sz = b.el.getSize();
31438 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31446 for (var i = 0; i < this.cols; i++){
31447 mY = Math.max(mY, maxY[i]);
31450 this.el.setHeight(mY - pos.y);
31454 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31456 // var pos = this.el.getBox(true);
31459 // var maxX = pos.right;
31461 // var maxHeight = 0;
31463 // Roo.each(items, function(item, k){
31467 // item.el.position('absolute');
31469 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31471 // item.el.setWidth(width);
31473 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31475 // item.el.setHeight(height);
31478 // item.el.setXY([x, y], isInstant ? false : true);
31480 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31483 // y = y + height + this.alternativePadWidth;
31485 // maxHeight = maxHeight + height + this.alternativePadWidth;
31489 // this.el.setHeight(maxHeight);
31493 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31495 var pos = this.el.getBox(true);
31500 var maxX = pos.right;
31502 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31504 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31506 Roo.each(queue, function(box, k){
31508 Roo.each(box, function(b, kk){
31510 b.el.position('absolute');
31512 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31513 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31515 if(b.size == 'md-left' || b.size == 'md-right'){
31516 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31517 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31520 b.el.setWidth(width);
31521 b.el.setHeight(height);
31529 var positions = [];
31531 switch (box.length){
31533 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31536 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31539 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31542 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31548 Roo.each(box, function(b,kk){
31550 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31552 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31560 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31562 Roo.each(eItems, function(b,k){
31564 b.size = (k == 0) ? 'sm' : 'xs';
31565 b.x = (k == 0) ? 2 : 1;
31566 b.y = (k == 0) ? 2 : 1;
31568 b.el.position('absolute');
31570 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31572 b.el.setWidth(width);
31574 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31576 b.el.setHeight(height);
31580 var positions = [];
31583 x : maxX - this.unitWidth * 2 - this.gutter,
31588 x : maxX - this.unitWidth,
31589 y : minY + (this.unitWidth + this.gutter) * 2
31593 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31597 Roo.each(eItems, function(b,k){
31599 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31605 getVerticalOneBoxColPositions : function(x, y, box)
31609 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31611 if(box[0].size == 'md-left'){
31615 if(box[0].size == 'md-right'){
31620 x : x + (this.unitWidth + this.gutter) * rand,
31627 getVerticalTwoBoxColPositions : function(x, y, box)
31631 if(box[0].size == 'xs'){
31635 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31639 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31653 x : x + (this.unitWidth + this.gutter) * 2,
31654 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31661 getVerticalThreeBoxColPositions : function(x, y, box)
31665 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31673 x : x + (this.unitWidth + this.gutter) * 1,
31678 x : x + (this.unitWidth + this.gutter) * 2,
31686 if(box[0].size == 'xs' && box[1].size == 'xs'){
31695 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31699 x : x + (this.unitWidth + this.gutter) * 1,
31713 x : x + (this.unitWidth + this.gutter) * 2,
31718 x : x + (this.unitWidth + this.gutter) * 2,
31719 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31726 getVerticalFourBoxColPositions : function(x, y, box)
31730 if(box[0].size == 'xs'){
31739 y : y + (this.unitHeight + this.gutter) * 1
31744 y : y + (this.unitHeight + this.gutter) * 2
31748 x : x + (this.unitWidth + this.gutter) * 1,
31762 x : x + (this.unitWidth + this.gutter) * 2,
31767 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31768 y : y + (this.unitHeight + this.gutter) * 1
31772 x : x + (this.unitWidth + this.gutter) * 2,
31773 y : y + (this.unitWidth + this.gutter) * 2
31780 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31784 if(box[0].size == 'md-left'){
31786 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31793 if(box[0].size == 'md-right'){
31795 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31796 y : minY + (this.unitWidth + this.gutter) * 1
31802 var rand = Math.floor(Math.random() * (4 - box[0].y));
31805 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31806 y : minY + (this.unitWidth + this.gutter) * rand
31813 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31817 if(box[0].size == 'xs'){
31820 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31825 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31826 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31834 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31839 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31840 y : minY + (this.unitWidth + this.gutter) * 2
31847 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31851 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31854 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31859 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31860 y : minY + (this.unitWidth + this.gutter) * 1
31864 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31865 y : minY + (this.unitWidth + this.gutter) * 2
31872 if(box[0].size == 'xs' && box[1].size == 'xs'){
31875 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31880 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31885 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31886 y : minY + (this.unitWidth + this.gutter) * 1
31894 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31899 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31900 y : minY + (this.unitWidth + this.gutter) * 2
31904 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31905 y : minY + (this.unitWidth + this.gutter) * 2
31912 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31916 if(box[0].size == 'xs'){
31919 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31924 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31929 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),
31934 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31935 y : minY + (this.unitWidth + this.gutter) * 1
31943 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31948 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31949 y : minY + (this.unitWidth + this.gutter) * 2
31953 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31954 y : minY + (this.unitWidth + this.gutter) * 2
31958 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),
31959 y : minY + (this.unitWidth + this.gutter) * 2
31967 * remove a Masonry Brick
31968 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31970 removeBrick : function(brick_id)
31976 for (var i = 0; i<this.bricks.length; i++) {
31977 if (this.bricks[i].id == brick_id) {
31978 this.bricks.splice(i,1);
31979 this.el.dom.removeChild(Roo.get(brick_id).dom);
31986 * adds a Masonry Brick
31987 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31989 addBrick : function(cfg)
31991 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31992 //this.register(cn);
31993 cn.parentId = this.id;
31994 cn.onRender(this.el, null);
31999 * register a Masonry Brick
32000 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32003 register : function(brick)
32005 this.bricks.push(brick);
32006 brick.masonryId = this.id;
32010 * clear all the Masonry Brick
32012 clearAll : function()
32015 //this.getChildContainer().dom.innerHTML = "";
32016 this.el.dom.innerHTML = '';
32019 getSelected : function()
32021 if (!this.selectedBrick) {
32025 return this.selectedBrick;
32029 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32033 * register a Masonry Layout
32034 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32037 register : function(layout)
32039 this.groups[layout.id] = layout;
32042 * fetch a Masonry Layout based on the masonry layout ID
32043 * @param {string} the masonry layout to add
32044 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32047 get: function(layout_id) {
32048 if (typeof(this.groups[layout_id]) == 'undefined') {
32051 return this.groups[layout_id] ;
32063 * http://masonry.desandro.com
32065 * The idea is to render all the bricks based on vertical width...
32067 * The original code extends 'outlayer' - we might need to use that....
32073 * @class Roo.bootstrap.LayoutMasonryAuto
32074 * @extends Roo.bootstrap.Component
32075 * Bootstrap Layout Masonry class
32078 * Create a new Element
32079 * @param {Object} config The config object
32082 Roo.bootstrap.LayoutMasonryAuto = function(config){
32083 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32086 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32089 * @cfg {Boolean} isFitWidth - resize the width..
32091 isFitWidth : false, // options..
32093 * @cfg {Boolean} isOriginLeft = left align?
32095 isOriginLeft : true,
32097 * @cfg {Boolean} isOriginTop = top align?
32099 isOriginTop : false,
32101 * @cfg {Boolean} isLayoutInstant = no animation?
32103 isLayoutInstant : false, // needed?
32105 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32107 isResizingContainer : true,
32109 * @cfg {Number} columnWidth width of the columns
32115 * @cfg {Number} maxCols maximum number of columns
32120 * @cfg {Number} padHeight padding below box..
32126 * @cfg {Boolean} isAutoInitial defalut true
32129 isAutoInitial : true,
32135 initialColumnWidth : 0,
32136 currentSize : null,
32138 colYs : null, // array.
32145 bricks: null, //CompositeElement
32146 cols : 0, // array?
32147 // element : null, // wrapped now this.el
32148 _isLayoutInited : null,
32151 getAutoCreate : function(){
32155 cls: 'blog-masonary-wrapper ' + this.cls,
32157 cls : 'mas-boxes masonary'
32164 getChildContainer: function( )
32166 if (this.boxesEl) {
32167 return this.boxesEl;
32170 this.boxesEl = this.el.select('.mas-boxes').first();
32172 return this.boxesEl;
32176 initEvents : function()
32180 if(this.isAutoInitial){
32181 Roo.log('hook children rendered');
32182 this.on('childrenrendered', function() {
32183 Roo.log('children rendered');
32190 initial : function()
32192 this.reloadItems();
32194 this.currentSize = this.el.getBox(true);
32196 /// was window resize... - let's see if this works..
32197 Roo.EventManager.onWindowResize(this.resize, this);
32199 if(!this.isAutoInitial){
32204 this.layout.defer(500,this);
32207 reloadItems: function()
32209 this.bricks = this.el.select('.masonry-brick', true);
32211 this.bricks.each(function(b) {
32212 //Roo.log(b.getSize());
32213 if (!b.attr('originalwidth')) {
32214 b.attr('originalwidth', b.getSize().width);
32219 Roo.log(this.bricks.elements.length);
32222 resize : function()
32225 var cs = this.el.getBox(true);
32227 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32228 Roo.log("no change in with or X");
32231 this.currentSize = cs;
32235 layout : function()
32238 this._resetLayout();
32239 //this._manageStamps();
32241 // don't animate first layout
32242 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32243 this.layoutItems( isInstant );
32245 // flag for initalized
32246 this._isLayoutInited = true;
32249 layoutItems : function( isInstant )
32251 //var items = this._getItemsForLayout( this.items );
32252 // original code supports filtering layout items.. we just ignore it..
32254 this._layoutItems( this.bricks , isInstant );
32256 this._postLayout();
32258 _layoutItems : function ( items , isInstant)
32260 //this.fireEvent( 'layout', this, items );
32263 if ( !items || !items.elements.length ) {
32264 // no items, emit event with empty array
32269 items.each(function(item) {
32270 Roo.log("layout item");
32272 // get x/y object from method
32273 var position = this._getItemLayoutPosition( item );
32275 position.item = item;
32276 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32277 queue.push( position );
32280 this._processLayoutQueue( queue );
32282 /** Sets position of item in DOM
32283 * @param {Element} item
32284 * @param {Number} x - horizontal position
32285 * @param {Number} y - vertical position
32286 * @param {Boolean} isInstant - disables transitions
32288 _processLayoutQueue : function( queue )
32290 for ( var i=0, len = queue.length; i < len; i++ ) {
32291 var obj = queue[i];
32292 obj.item.position('absolute');
32293 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32299 * Any logic you want to do after each layout,
32300 * i.e. size the container
32302 _postLayout : function()
32304 this.resizeContainer();
32307 resizeContainer : function()
32309 if ( !this.isResizingContainer ) {
32312 var size = this._getContainerSize();
32314 this.el.setSize(size.width,size.height);
32315 this.boxesEl.setSize(size.width,size.height);
32321 _resetLayout : function()
32323 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32324 this.colWidth = this.el.getWidth();
32325 //this.gutter = this.el.getWidth();
32327 this.measureColumns();
32333 this.colYs.push( 0 );
32339 measureColumns : function()
32341 this.getContainerWidth();
32342 // if columnWidth is 0, default to outerWidth of first item
32343 if ( !this.columnWidth ) {
32344 var firstItem = this.bricks.first();
32345 Roo.log(firstItem);
32346 this.columnWidth = this.containerWidth;
32347 if (firstItem && firstItem.attr('originalwidth') ) {
32348 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32350 // columnWidth fall back to item of first element
32351 Roo.log("set column width?");
32352 this.initialColumnWidth = this.columnWidth ;
32354 // if first elem has no width, default to size of container
32359 if (this.initialColumnWidth) {
32360 this.columnWidth = this.initialColumnWidth;
32365 // column width is fixed at the top - however if container width get's smaller we should
32368 // this bit calcs how man columns..
32370 var columnWidth = this.columnWidth += this.gutter;
32372 // calculate columns
32373 var containerWidth = this.containerWidth + this.gutter;
32375 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32376 // fix rounding errors, typically with gutters
32377 var excess = columnWidth - containerWidth % columnWidth;
32380 // if overshoot is less than a pixel, round up, otherwise floor it
32381 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32382 cols = Math[ mathMethod ]( cols );
32383 this.cols = Math.max( cols, 1 );
32384 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32386 // padding positioning..
32387 var totalColWidth = this.cols * this.columnWidth;
32388 var padavail = this.containerWidth - totalColWidth;
32389 // so for 2 columns - we need 3 'pads'
32391 var padNeeded = (1+this.cols) * this.padWidth;
32393 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32395 this.columnWidth += padExtra
32396 //this.padWidth = Math.floor(padavail / ( this.cols));
32398 // adjust colum width so that padding is fixed??
32400 // we have 3 columns ... total = width * 3
32401 // we have X left over... that should be used by
32403 //if (this.expandC) {
32411 getContainerWidth : function()
32413 /* // container is parent if fit width
32414 var container = this.isFitWidth ? this.element.parentNode : this.element;
32415 // check that this.size and size are there
32416 // IE8 triggers resize on body size change, so they might not be
32418 var size = getSize( container ); //FIXME
32419 this.containerWidth = size && size.innerWidth; //FIXME
32422 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32426 _getItemLayoutPosition : function( item ) // what is item?
32428 // we resize the item to our columnWidth..
32430 item.setWidth(this.columnWidth);
32431 item.autoBoxAdjust = false;
32433 var sz = item.getSize();
32435 // how many columns does this brick span
32436 var remainder = this.containerWidth % this.columnWidth;
32438 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32439 // round if off by 1 pixel, otherwise use ceil
32440 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32441 colSpan = Math.min( colSpan, this.cols );
32443 // normally this should be '1' as we dont' currently allow multi width columns..
32445 var colGroup = this._getColGroup( colSpan );
32446 // get the minimum Y value from the columns
32447 var minimumY = Math.min.apply( Math, colGroup );
32448 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32450 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32452 // position the brick
32454 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32455 y: this.currentSize.y + minimumY + this.padHeight
32459 // apply setHeight to necessary columns
32460 var setHeight = minimumY + sz.height + this.padHeight;
32461 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32463 var setSpan = this.cols + 1 - colGroup.length;
32464 for ( var i = 0; i < setSpan; i++ ) {
32465 this.colYs[ shortColIndex + i ] = setHeight ;
32472 * @param {Number} colSpan - number of columns the element spans
32473 * @returns {Array} colGroup
32475 _getColGroup : function( colSpan )
32477 if ( colSpan < 2 ) {
32478 // if brick spans only one column, use all the column Ys
32483 // how many different places could this brick fit horizontally
32484 var groupCount = this.cols + 1 - colSpan;
32485 // for each group potential horizontal position
32486 for ( var i = 0; i < groupCount; i++ ) {
32487 // make an array of colY values for that one group
32488 var groupColYs = this.colYs.slice( i, i + colSpan );
32489 // and get the max value of the array
32490 colGroup[i] = Math.max.apply( Math, groupColYs );
32495 _manageStamp : function( stamp )
32497 var stampSize = stamp.getSize();
32498 var offset = stamp.getBox();
32499 // get the columns that this stamp affects
32500 var firstX = this.isOriginLeft ? offset.x : offset.right;
32501 var lastX = firstX + stampSize.width;
32502 var firstCol = Math.floor( firstX / this.columnWidth );
32503 firstCol = Math.max( 0, firstCol );
32505 var lastCol = Math.floor( lastX / this.columnWidth );
32506 // lastCol should not go over if multiple of columnWidth #425
32507 lastCol -= lastX % this.columnWidth ? 0 : 1;
32508 lastCol = Math.min( this.cols - 1, lastCol );
32510 // set colYs to bottom of the stamp
32511 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32514 for ( var i = firstCol; i <= lastCol; i++ ) {
32515 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32520 _getContainerSize : function()
32522 this.maxY = Math.max.apply( Math, this.colYs );
32527 if ( this.isFitWidth ) {
32528 size.width = this._getContainerFitWidth();
32534 _getContainerFitWidth : function()
32536 var unusedCols = 0;
32537 // count unused columns
32540 if ( this.colYs[i] !== 0 ) {
32545 // fit container to columns that have been used
32546 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32549 needsResizeLayout : function()
32551 var previousWidth = this.containerWidth;
32552 this.getContainerWidth();
32553 return previousWidth !== this.containerWidth;
32568 * @class Roo.bootstrap.MasonryBrick
32569 * @extends Roo.bootstrap.Component
32570 * Bootstrap MasonryBrick class
32573 * Create a new MasonryBrick
32574 * @param {Object} config The config object
32577 Roo.bootstrap.MasonryBrick = function(config){
32579 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32581 Roo.bootstrap.MasonryBrick.register(this);
32587 * When a MasonryBrick is clcik
32588 * @param {Roo.bootstrap.MasonryBrick} this
32589 * @param {Roo.EventObject} e
32595 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32598 * @cfg {String} title
32602 * @cfg {String} html
32606 * @cfg {String} bgimage
32610 * @cfg {String} videourl
32614 * @cfg {String} cls
32618 * @cfg {String} href
32622 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32627 * @cfg {String} placetitle (center|bottom)
32632 * @cfg {Boolean} isFitContainer defalut true
32634 isFitContainer : true,
32637 * @cfg {Boolean} preventDefault defalut false
32639 preventDefault : false,
32642 * @cfg {Boolean} inverse defalut false
32644 maskInverse : false,
32646 getAutoCreate : function()
32648 if(!this.isFitContainer){
32649 return this.getSplitAutoCreate();
32652 var cls = 'masonry-brick masonry-brick-full';
32654 if(this.href.length){
32655 cls += ' masonry-brick-link';
32658 if(this.bgimage.length){
32659 cls += ' masonry-brick-image';
32662 if(this.maskInverse){
32663 cls += ' mask-inverse';
32666 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32667 cls += ' enable-mask';
32671 cls += ' masonry-' + this.size + '-brick';
32674 if(this.placetitle.length){
32676 switch (this.placetitle) {
32678 cls += ' masonry-center-title';
32681 cls += ' masonry-bottom-title';
32688 if(!this.html.length && !this.bgimage.length){
32689 cls += ' masonry-center-title';
32692 if(!this.html.length && this.bgimage.length){
32693 cls += ' masonry-bottom-title';
32698 cls += ' ' + this.cls;
32702 tag: (this.href.length) ? 'a' : 'div',
32707 cls: 'masonry-brick-mask'
32711 cls: 'masonry-brick-paragraph',
32717 if(this.href.length){
32718 cfg.href = this.href;
32721 var cn = cfg.cn[1].cn;
32723 if(this.title.length){
32726 cls: 'masonry-brick-title',
32731 if(this.html.length){
32734 cls: 'masonry-brick-text',
32739 if (!this.title.length && !this.html.length) {
32740 cfg.cn[1].cls += ' hide';
32743 if(this.bgimage.length){
32746 cls: 'masonry-brick-image-view',
32751 if(this.videourl.length){
32752 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32753 // youtube support only?
32756 cls: 'masonry-brick-image-view',
32759 allowfullscreen : true
32767 getSplitAutoCreate : function()
32769 var cls = 'masonry-brick masonry-brick-split';
32771 if(this.href.length){
32772 cls += ' masonry-brick-link';
32775 if(this.bgimage.length){
32776 cls += ' masonry-brick-image';
32780 cls += ' masonry-' + this.size + '-brick';
32783 switch (this.placetitle) {
32785 cls += ' masonry-center-title';
32788 cls += ' masonry-bottom-title';
32791 if(!this.bgimage.length){
32792 cls += ' masonry-center-title';
32795 if(this.bgimage.length){
32796 cls += ' masonry-bottom-title';
32802 cls += ' ' + this.cls;
32806 tag: (this.href.length) ? 'a' : 'div',
32811 cls: 'masonry-brick-split-head',
32815 cls: 'masonry-brick-paragraph',
32822 cls: 'masonry-brick-split-body',
32828 if(this.href.length){
32829 cfg.href = this.href;
32832 if(this.title.length){
32833 cfg.cn[0].cn[0].cn.push({
32835 cls: 'masonry-brick-title',
32840 if(this.html.length){
32841 cfg.cn[1].cn.push({
32843 cls: 'masonry-brick-text',
32848 if(this.bgimage.length){
32849 cfg.cn[0].cn.push({
32851 cls: 'masonry-brick-image-view',
32856 if(this.videourl.length){
32857 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32858 // youtube support only?
32859 cfg.cn[0].cn.cn.push({
32861 cls: 'masonry-brick-image-view',
32864 allowfullscreen : true
32871 initEvents: function()
32873 switch (this.size) {
32906 this.el.on('touchstart', this.onTouchStart, this);
32907 this.el.on('touchmove', this.onTouchMove, this);
32908 this.el.on('touchend', this.onTouchEnd, this);
32909 this.el.on('contextmenu', this.onContextMenu, this);
32911 this.el.on('mouseenter' ,this.enter, this);
32912 this.el.on('mouseleave', this.leave, this);
32913 this.el.on('click', this.onClick, this);
32916 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32917 this.parent().bricks.push(this);
32922 onClick: function(e, el)
32924 var time = this.endTimer - this.startTimer;
32925 // Roo.log(e.preventDefault());
32928 e.preventDefault();
32933 if(!this.preventDefault){
32937 e.preventDefault();
32939 if (this.activeClass != '') {
32940 this.selectBrick();
32943 this.fireEvent('click', this, e);
32946 enter: function(e, el)
32948 e.preventDefault();
32950 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32954 if(this.bgimage.length && this.html.length){
32955 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32959 leave: function(e, el)
32961 e.preventDefault();
32963 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32967 if(this.bgimage.length && this.html.length){
32968 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32972 onTouchStart: function(e, el)
32974 // e.preventDefault();
32976 this.touchmoved = false;
32978 if(!this.isFitContainer){
32982 if(!this.bgimage.length || !this.html.length){
32986 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32988 this.timer = new Date().getTime();
32992 onTouchMove: function(e, el)
32994 this.touchmoved = true;
32997 onContextMenu : function(e,el)
32999 e.preventDefault();
33000 e.stopPropagation();
33004 onTouchEnd: function(e, el)
33006 // e.preventDefault();
33008 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33015 if(!this.bgimage.length || !this.html.length){
33017 if(this.href.length){
33018 window.location.href = this.href;
33024 if(!this.isFitContainer){
33028 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33030 window.location.href = this.href;
33033 //selection on single brick only
33034 selectBrick : function() {
33036 if (!this.parentId) {
33040 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33041 var index = m.selectedBrick.indexOf(this.id);
33044 m.selectedBrick.splice(index,1);
33045 this.el.removeClass(this.activeClass);
33049 for(var i = 0; i < m.selectedBrick.length; i++) {
33050 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33051 b.el.removeClass(b.activeClass);
33054 m.selectedBrick = [];
33056 m.selectedBrick.push(this.id);
33057 this.el.addClass(this.activeClass);
33061 isSelected : function(){
33062 return this.el.hasClass(this.activeClass);
33067 Roo.apply(Roo.bootstrap.MasonryBrick, {
33070 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33072 * register a Masonry Brick
33073 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33076 register : function(brick)
33078 //this.groups[brick.id] = brick;
33079 this.groups.add(brick.id, brick);
33082 * fetch a masonry brick based on the masonry brick ID
33083 * @param {string} the masonry brick to add
33084 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33087 get: function(brick_id)
33089 // if (typeof(this.groups[brick_id]) == 'undefined') {
33092 // return this.groups[brick_id] ;
33094 if(this.groups.key(brick_id)) {
33095 return this.groups.key(brick_id);
33113 * @class Roo.bootstrap.Brick
33114 * @extends Roo.bootstrap.Component
33115 * Bootstrap Brick class
33118 * Create a new Brick
33119 * @param {Object} config The config object
33122 Roo.bootstrap.Brick = function(config){
33123 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33129 * When a Brick is click
33130 * @param {Roo.bootstrap.Brick} this
33131 * @param {Roo.EventObject} e
33137 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33140 * @cfg {String} title
33144 * @cfg {String} html
33148 * @cfg {String} bgimage
33152 * @cfg {String} cls
33156 * @cfg {String} href
33160 * @cfg {String} video
33164 * @cfg {Boolean} square
33168 getAutoCreate : function()
33170 var cls = 'roo-brick';
33172 if(this.href.length){
33173 cls += ' roo-brick-link';
33176 if(this.bgimage.length){
33177 cls += ' roo-brick-image';
33180 if(!this.html.length && !this.bgimage.length){
33181 cls += ' roo-brick-center-title';
33184 if(!this.html.length && this.bgimage.length){
33185 cls += ' roo-brick-bottom-title';
33189 cls += ' ' + this.cls;
33193 tag: (this.href.length) ? 'a' : 'div',
33198 cls: 'roo-brick-paragraph',
33204 if(this.href.length){
33205 cfg.href = this.href;
33208 var cn = cfg.cn[0].cn;
33210 if(this.title.length){
33213 cls: 'roo-brick-title',
33218 if(this.html.length){
33221 cls: 'roo-brick-text',
33228 if(this.bgimage.length){
33231 cls: 'roo-brick-image-view',
33239 initEvents: function()
33241 if(this.title.length || this.html.length){
33242 this.el.on('mouseenter' ,this.enter, this);
33243 this.el.on('mouseleave', this.leave, this);
33246 Roo.EventManager.onWindowResize(this.resize, this);
33248 if(this.bgimage.length){
33249 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33250 this.imageEl.on('load', this.onImageLoad, this);
33257 onImageLoad : function()
33262 resize : function()
33264 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33266 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33268 if(this.bgimage.length){
33269 var image = this.el.select('.roo-brick-image-view', true).first();
33271 image.setWidth(paragraph.getWidth());
33274 image.setHeight(paragraph.getWidth());
33277 this.el.setHeight(image.getHeight());
33278 paragraph.setHeight(image.getHeight());
33284 enter: function(e, el)
33286 e.preventDefault();
33288 if(this.bgimage.length){
33289 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33290 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33294 leave: function(e, el)
33296 e.preventDefault();
33298 if(this.bgimage.length){
33299 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33300 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33315 * @class Roo.bootstrap.NumberField
33316 * @extends Roo.bootstrap.Input
33317 * Bootstrap NumberField class
33323 * Create a new NumberField
33324 * @param {Object} config The config object
33327 Roo.bootstrap.NumberField = function(config){
33328 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33331 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33334 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33336 allowDecimals : true,
33338 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33340 decimalSeparator : ".",
33342 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33344 decimalPrecision : 2,
33346 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33348 allowNegative : true,
33351 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33355 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33357 minValue : Number.NEGATIVE_INFINITY,
33359 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33361 maxValue : Number.MAX_VALUE,
33363 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33365 minText : "The minimum value for this field is {0}",
33367 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33369 maxText : "The maximum value for this field is {0}",
33371 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33372 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33374 nanText : "{0} is not a valid number",
33376 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33378 thousandsDelimiter : false,
33380 * @cfg {String} valueAlign alignment of value
33382 valueAlign : "left",
33384 getAutoCreate : function()
33386 var hiddenInput = {
33390 cls: 'hidden-number-input'
33394 hiddenInput.name = this.name;
33399 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33401 this.name = hiddenInput.name;
33403 if(cfg.cn.length > 0) {
33404 cfg.cn.push(hiddenInput);
33411 initEvents : function()
33413 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33415 var allowed = "0123456789";
33417 if(this.allowDecimals){
33418 allowed += this.decimalSeparator;
33421 if(this.allowNegative){
33425 if(this.thousandsDelimiter) {
33429 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33431 var keyPress = function(e){
33433 var k = e.getKey();
33435 var c = e.getCharCode();
33438 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33439 allowed.indexOf(String.fromCharCode(c)) === -1
33445 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33449 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33454 this.el.on("keypress", keyPress, this);
33457 validateValue : function(value)
33460 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33464 var num = this.parseValue(value);
33467 this.markInvalid(String.format(this.nanText, value));
33471 if(num < this.minValue){
33472 this.markInvalid(String.format(this.minText, this.minValue));
33476 if(num > this.maxValue){
33477 this.markInvalid(String.format(this.maxText, this.maxValue));
33484 getValue : function()
33486 var v = this.hiddenEl().getValue();
33488 return this.fixPrecision(this.parseValue(v));
33491 parseValue : function(value)
33493 if(this.thousandsDelimiter) {
33495 r = new RegExp(",", "g");
33496 value = value.replace(r, "");
33499 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33500 return isNaN(value) ? '' : value;
33503 fixPrecision : function(value)
33505 if(this.thousandsDelimiter) {
33507 r = new RegExp(",", "g");
33508 value = value.replace(r, "");
33511 var nan = isNaN(value);
33513 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33514 return nan ? '' : value;
33516 return parseFloat(value).toFixed(this.decimalPrecision);
33519 setValue : function(v)
33521 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33527 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33529 this.inputEl().dom.value = (v == '') ? '' :
33530 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33532 if(!this.allowZero && v === '0') {
33533 this.hiddenEl().dom.value = '';
33534 this.inputEl().dom.value = '';
33541 decimalPrecisionFcn : function(v)
33543 return Math.floor(v);
33546 beforeBlur : function()
33548 var v = this.parseValue(this.getRawValue());
33550 if(v || v === 0 || v === ''){
33555 hiddenEl : function()
33557 return this.el.select('input.hidden-number-input',true).first();
33569 * @class Roo.bootstrap.DocumentSlider
33570 * @extends Roo.bootstrap.Component
33571 * Bootstrap DocumentSlider class
33574 * Create a new DocumentViewer
33575 * @param {Object} config The config object
33578 Roo.bootstrap.DocumentSlider = function(config){
33579 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33586 * Fire after initEvent
33587 * @param {Roo.bootstrap.DocumentSlider} this
33592 * Fire after update
33593 * @param {Roo.bootstrap.DocumentSlider} this
33599 * @param {Roo.bootstrap.DocumentSlider} this
33605 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33611 getAutoCreate : function()
33615 cls : 'roo-document-slider',
33619 cls : 'roo-document-slider-header',
33623 cls : 'roo-document-slider-header-title'
33629 cls : 'roo-document-slider-body',
33633 cls : 'roo-document-slider-prev',
33637 cls : 'fa fa-chevron-left'
33643 cls : 'roo-document-slider-thumb',
33647 cls : 'roo-document-slider-image'
33653 cls : 'roo-document-slider-next',
33657 cls : 'fa fa-chevron-right'
33669 initEvents : function()
33671 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33672 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33674 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33675 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33677 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33678 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33680 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33681 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33683 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33684 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33686 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33687 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33689 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33690 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33692 this.thumbEl.on('click', this.onClick, this);
33694 this.prevIndicator.on('click', this.prev, this);
33696 this.nextIndicator.on('click', this.next, this);
33700 initial : function()
33702 if(this.files.length){
33703 this.indicator = 1;
33707 this.fireEvent('initial', this);
33710 update : function()
33712 this.imageEl.attr('src', this.files[this.indicator - 1]);
33714 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33716 this.prevIndicator.show();
33718 if(this.indicator == 1){
33719 this.prevIndicator.hide();
33722 this.nextIndicator.show();
33724 if(this.indicator == this.files.length){
33725 this.nextIndicator.hide();
33728 this.thumbEl.scrollTo('top');
33730 this.fireEvent('update', this);
33733 onClick : function(e)
33735 e.preventDefault();
33737 this.fireEvent('click', this);
33742 e.preventDefault();
33744 this.indicator = Math.max(1, this.indicator - 1);
33751 e.preventDefault();
33753 this.indicator = Math.min(this.files.length, this.indicator + 1);
33767 * @class Roo.bootstrap.RadioSet
33768 * @extends Roo.bootstrap.Input
33769 * Bootstrap RadioSet class
33770 * @cfg {String} indicatorpos (left|right) default left
33771 * @cfg {Boolean} inline (true|false) inline the element (default true)
33772 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33774 * Create a new RadioSet
33775 * @param {Object} config The config object
33778 Roo.bootstrap.RadioSet = function(config){
33780 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33784 Roo.bootstrap.RadioSet.register(this);
33789 * Fires when the element is checked or unchecked.
33790 * @param {Roo.bootstrap.RadioSet} this This radio
33791 * @param {Roo.bootstrap.Radio} item The checked item
33796 * Fires when the element is click.
33797 * @param {Roo.bootstrap.RadioSet} this This radio set
33798 * @param {Roo.bootstrap.Radio} item The checked item
33799 * @param {Roo.EventObject} e The event object
33806 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33814 indicatorpos : 'left',
33816 getAutoCreate : function()
33820 cls : 'roo-radio-set-label',
33824 html : this.fieldLabel
33829 if(this.indicatorpos == 'left'){
33832 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33833 tooltip : 'This field is required'
33838 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33839 tooltip : 'This field is required'
33845 cls : 'roo-radio-set-items'
33848 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33850 if (align === 'left' && this.fieldLabel.length) {
33853 cls : "roo-radio-set-right",
33859 if(this.labelWidth > 12){
33860 label.style = "width: " + this.labelWidth + 'px';
33863 if(this.labelWidth < 13 && this.labelmd == 0){
33864 this.labelmd = this.labelWidth;
33867 if(this.labellg > 0){
33868 label.cls += ' col-lg-' + this.labellg;
33869 items.cls += ' col-lg-' + (12 - this.labellg);
33872 if(this.labelmd > 0){
33873 label.cls += ' col-md-' + this.labelmd;
33874 items.cls += ' col-md-' + (12 - this.labelmd);
33877 if(this.labelsm > 0){
33878 label.cls += ' col-sm-' + this.labelsm;
33879 items.cls += ' col-sm-' + (12 - this.labelsm);
33882 if(this.labelxs > 0){
33883 label.cls += ' col-xs-' + this.labelxs;
33884 items.cls += ' col-xs-' + (12 - this.labelxs);
33890 cls : 'roo-radio-set',
33894 cls : 'roo-radio-set-input',
33897 value : this.value ? this.value : ''
33904 if(this.weight.length){
33905 cfg.cls += ' roo-radio-' + this.weight;
33909 cfg.cls += ' roo-radio-set-inline';
33913 ['xs','sm','md','lg'].map(function(size){
33914 if (settings[size]) {
33915 cfg.cls += ' col-' + size + '-' + settings[size];
33923 initEvents : function()
33925 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33926 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33928 if(!this.fieldLabel.length){
33929 this.labelEl.hide();
33932 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33933 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33935 this.indicator = this.indicatorEl();
33937 if(this.indicator){
33938 this.indicator.addClass('invisible');
33941 this.originalValue = this.getValue();
33945 inputEl: function ()
33947 return this.el.select('.roo-radio-set-input', true).first();
33950 getChildContainer : function()
33952 return this.itemsEl;
33955 register : function(item)
33957 this.radioes.push(item);
33961 validate : function()
33963 if(this.getVisibilityEl().hasClass('hidden')){
33969 Roo.each(this.radioes, function(i){
33978 if(this.allowBlank) {
33982 if(this.disabled || valid){
33987 this.markInvalid();
33992 markValid : function()
33994 if(this.labelEl.isVisible(true)){
33995 this.indicatorEl().removeClass('visible');
33996 this.indicatorEl().addClass('invisible');
33999 this.el.removeClass([this.invalidClass, this.validClass]);
34000 this.el.addClass(this.validClass);
34002 this.fireEvent('valid', this);
34005 markInvalid : function(msg)
34007 if(this.allowBlank || this.disabled){
34011 if(this.labelEl.isVisible(true)){
34012 this.indicatorEl().removeClass('invisible');
34013 this.indicatorEl().addClass('visible');
34016 this.el.removeClass([this.invalidClass, this.validClass]);
34017 this.el.addClass(this.invalidClass);
34019 this.fireEvent('invalid', this, msg);
34023 setValue : function(v, suppressEvent)
34025 if(this.value === v){
34032 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34035 Roo.each(this.radioes, function(i){
34037 i.el.removeClass('checked');
34040 Roo.each(this.radioes, function(i){
34042 if(i.value === v || i.value.toString() === v.toString()){
34044 i.el.addClass('checked');
34046 if(suppressEvent !== true){
34047 this.fireEvent('check', this, i);
34058 clearInvalid : function(){
34060 if(!this.el || this.preventMark){
34064 this.el.removeClass([this.invalidClass]);
34066 this.fireEvent('valid', this);
34071 Roo.apply(Roo.bootstrap.RadioSet, {
34075 register : function(set)
34077 this.groups[set.name] = set;
34080 get: function(name)
34082 if (typeof(this.groups[name]) == 'undefined') {
34086 return this.groups[name] ;
34092 * Ext JS Library 1.1.1
34093 * Copyright(c) 2006-2007, Ext JS, LLC.
34095 * Originally Released Under LGPL - original licence link has changed is not relivant.
34098 * <script type="text/javascript">
34103 * @class Roo.bootstrap.SplitBar
34104 * @extends Roo.util.Observable
34105 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34109 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34110 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34111 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34112 split.minSize = 100;
34113 split.maxSize = 600;
34114 split.animate = true;
34115 split.on('moved', splitterMoved);
34118 * Create a new SplitBar
34119 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34120 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34121 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34122 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34123 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34124 position of the SplitBar).
34126 Roo.bootstrap.SplitBar = function(cfg){
34131 // dragElement : elm
34132 // resizingElement: el,
34134 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34135 // placement : Roo.bootstrap.SplitBar.LEFT ,
34136 // existingProxy ???
34139 this.el = Roo.get(cfg.dragElement, true);
34140 this.el.dom.unselectable = "on";
34142 this.resizingEl = Roo.get(cfg.resizingElement, true);
34146 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34147 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34150 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34153 * The minimum size of the resizing element. (Defaults to 0)
34159 * The maximum size of the resizing element. (Defaults to 2000)
34162 this.maxSize = 2000;
34165 * Whether to animate the transition to the new size
34168 this.animate = false;
34171 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34174 this.useShim = false;
34179 if(!cfg.existingProxy){
34181 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34183 this.proxy = Roo.get(cfg.existingProxy).dom;
34186 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34189 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34192 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34195 this.dragSpecs = {};
34198 * @private The adapter to use to positon and resize elements
34200 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34201 this.adapter.init(this);
34203 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34205 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34206 this.el.addClass("roo-splitbar-h");
34209 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34210 this.el.addClass("roo-splitbar-v");
34216 * Fires when the splitter is moved (alias for {@link #event-moved})
34217 * @param {Roo.bootstrap.SplitBar} this
34218 * @param {Number} newSize the new width or height
34223 * Fires when the splitter is moved
34224 * @param {Roo.bootstrap.SplitBar} this
34225 * @param {Number} newSize the new width or height
34229 * @event beforeresize
34230 * Fires before the splitter is dragged
34231 * @param {Roo.bootstrap.SplitBar} this
34233 "beforeresize" : true,
34235 "beforeapply" : true
34238 Roo.util.Observable.call(this);
34241 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34242 onStartProxyDrag : function(x, y){
34243 this.fireEvent("beforeresize", this);
34245 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34247 o.enableDisplayMode("block");
34248 // all splitbars share the same overlay
34249 Roo.bootstrap.SplitBar.prototype.overlay = o;
34251 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34252 this.overlay.show();
34253 Roo.get(this.proxy).setDisplayed("block");
34254 var size = this.adapter.getElementSize(this);
34255 this.activeMinSize = this.getMinimumSize();;
34256 this.activeMaxSize = this.getMaximumSize();;
34257 var c1 = size - this.activeMinSize;
34258 var c2 = Math.max(this.activeMaxSize - size, 0);
34259 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34260 this.dd.resetConstraints();
34261 this.dd.setXConstraint(
34262 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34263 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34265 this.dd.setYConstraint(0, 0);
34267 this.dd.resetConstraints();
34268 this.dd.setXConstraint(0, 0);
34269 this.dd.setYConstraint(
34270 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34271 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34274 this.dragSpecs.startSize = size;
34275 this.dragSpecs.startPoint = [x, y];
34276 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34280 * @private Called after the drag operation by the DDProxy
34282 onEndProxyDrag : function(e){
34283 Roo.get(this.proxy).setDisplayed(false);
34284 var endPoint = Roo.lib.Event.getXY(e);
34286 this.overlay.hide();
34289 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34290 newSize = this.dragSpecs.startSize +
34291 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34292 endPoint[0] - this.dragSpecs.startPoint[0] :
34293 this.dragSpecs.startPoint[0] - endPoint[0]
34296 newSize = this.dragSpecs.startSize +
34297 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34298 endPoint[1] - this.dragSpecs.startPoint[1] :
34299 this.dragSpecs.startPoint[1] - endPoint[1]
34302 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34303 if(newSize != this.dragSpecs.startSize){
34304 if(this.fireEvent('beforeapply', this, newSize) !== false){
34305 this.adapter.setElementSize(this, newSize);
34306 this.fireEvent("moved", this, newSize);
34307 this.fireEvent("resize", this, newSize);
34313 * Get the adapter this SplitBar uses
34314 * @return The adapter object
34316 getAdapter : function(){
34317 return this.adapter;
34321 * Set the adapter this SplitBar uses
34322 * @param {Object} adapter A SplitBar adapter object
34324 setAdapter : function(adapter){
34325 this.adapter = adapter;
34326 this.adapter.init(this);
34330 * Gets the minimum size for the resizing element
34331 * @return {Number} The minimum size
34333 getMinimumSize : function(){
34334 return this.minSize;
34338 * Sets the minimum size for the resizing element
34339 * @param {Number} minSize The minimum size
34341 setMinimumSize : function(minSize){
34342 this.minSize = minSize;
34346 * Gets the maximum size for the resizing element
34347 * @return {Number} The maximum size
34349 getMaximumSize : function(){
34350 return this.maxSize;
34354 * Sets the maximum size for the resizing element
34355 * @param {Number} maxSize The maximum size
34357 setMaximumSize : function(maxSize){
34358 this.maxSize = maxSize;
34362 * Sets the initialize size for the resizing element
34363 * @param {Number} size The initial size
34365 setCurrentSize : function(size){
34366 var oldAnimate = this.animate;
34367 this.animate = false;
34368 this.adapter.setElementSize(this, size);
34369 this.animate = oldAnimate;
34373 * Destroy this splitbar.
34374 * @param {Boolean} removeEl True to remove the element
34376 destroy : function(removeEl){
34378 this.shim.remove();
34381 this.proxy.parentNode.removeChild(this.proxy);
34389 * @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.
34391 Roo.bootstrap.SplitBar.createProxy = function(dir){
34392 var proxy = new Roo.Element(document.createElement("div"));
34393 proxy.unselectable();
34394 var cls = 'roo-splitbar-proxy';
34395 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34396 document.body.appendChild(proxy.dom);
34401 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34402 * Default Adapter. It assumes the splitter and resizing element are not positioned
34403 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34405 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34408 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34409 // do nothing for now
34410 init : function(s){
34414 * Called before drag operations to get the current size of the resizing element.
34415 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34417 getElementSize : function(s){
34418 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34419 return s.resizingEl.getWidth();
34421 return s.resizingEl.getHeight();
34426 * Called after drag operations to set the size of the resizing element.
34427 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34428 * @param {Number} newSize The new size to set
34429 * @param {Function} onComplete A function to be invoked when resizing is complete
34431 setElementSize : function(s, newSize, onComplete){
34432 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34434 s.resizingEl.setWidth(newSize);
34436 onComplete(s, newSize);
34439 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34444 s.resizingEl.setHeight(newSize);
34446 onComplete(s, newSize);
34449 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34456 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34457 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34458 * Adapter that moves the splitter element to align with the resized sizing element.
34459 * Used with an absolute positioned SplitBar.
34460 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34461 * document.body, make sure you assign an id to the body element.
34463 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34464 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34465 this.container = Roo.get(container);
34468 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34469 init : function(s){
34470 this.basic.init(s);
34473 getElementSize : function(s){
34474 return this.basic.getElementSize(s);
34477 setElementSize : function(s, newSize, onComplete){
34478 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34481 moveSplitter : function(s){
34482 var yes = Roo.bootstrap.SplitBar;
34483 switch(s.placement){
34485 s.el.setX(s.resizingEl.getRight());
34488 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34491 s.el.setY(s.resizingEl.getBottom());
34494 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34501 * Orientation constant - Create a vertical SplitBar
34505 Roo.bootstrap.SplitBar.VERTICAL = 1;
34508 * Orientation constant - Create a horizontal SplitBar
34512 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34515 * Placement constant - The resizing element is to the left of the splitter element
34519 Roo.bootstrap.SplitBar.LEFT = 1;
34522 * Placement constant - The resizing element is to the right of the splitter element
34526 Roo.bootstrap.SplitBar.RIGHT = 2;
34529 * Placement constant - The resizing element is positioned above the splitter element
34533 Roo.bootstrap.SplitBar.TOP = 3;
34536 * Placement constant - The resizing element is positioned under splitter element
34540 Roo.bootstrap.SplitBar.BOTTOM = 4;
34541 Roo.namespace("Roo.bootstrap.layout");/*
34543 * Ext JS Library 1.1.1
34544 * Copyright(c) 2006-2007, Ext JS, LLC.
34546 * Originally Released Under LGPL - original licence link has changed is not relivant.
34549 * <script type="text/javascript">
34553 * @class Roo.bootstrap.layout.Manager
34554 * @extends Roo.bootstrap.Component
34555 * Base class for layout managers.
34557 Roo.bootstrap.layout.Manager = function(config)
34559 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34565 /** false to disable window resize monitoring @type Boolean */
34566 this.monitorWindowResize = true;
34571 * Fires when a layout is performed.
34572 * @param {Roo.LayoutManager} this
34576 * @event regionresized
34577 * Fires when the user resizes a region.
34578 * @param {Roo.LayoutRegion} region The resized region
34579 * @param {Number} newSize The new size (width for east/west, height for north/south)
34581 "regionresized" : true,
34583 * @event regioncollapsed
34584 * Fires when a region is collapsed.
34585 * @param {Roo.LayoutRegion} region The collapsed region
34587 "regioncollapsed" : true,
34589 * @event regionexpanded
34590 * Fires when a region is expanded.
34591 * @param {Roo.LayoutRegion} region The expanded region
34593 "regionexpanded" : true
34595 this.updating = false;
34598 this.el = Roo.get(config.el);
34604 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34609 monitorWindowResize : true,
34615 onRender : function(ct, position)
34618 this.el = Roo.get(ct);
34621 //this.fireEvent('render',this);
34625 initEvents: function()
34629 // ie scrollbar fix
34630 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34631 document.body.scroll = "no";
34632 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34633 this.el.position('relative');
34635 this.id = this.el.id;
34636 this.el.addClass("roo-layout-container");
34637 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34638 if(this.el.dom != document.body ) {
34639 this.el.on('resize', this.layout,this);
34640 this.el.on('show', this.layout,this);
34646 * Returns true if this layout is currently being updated
34647 * @return {Boolean}
34649 isUpdating : function(){
34650 return this.updating;
34654 * Suspend the LayoutManager from doing auto-layouts while
34655 * making multiple add or remove calls
34657 beginUpdate : function(){
34658 this.updating = true;
34662 * Restore auto-layouts and optionally disable the manager from performing a layout
34663 * @param {Boolean} noLayout true to disable a layout update
34665 endUpdate : function(noLayout){
34666 this.updating = false;
34672 layout: function(){
34676 onRegionResized : function(region, newSize){
34677 this.fireEvent("regionresized", region, newSize);
34681 onRegionCollapsed : function(region){
34682 this.fireEvent("regioncollapsed", region);
34685 onRegionExpanded : function(region){
34686 this.fireEvent("regionexpanded", region);
34690 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34691 * performs box-model adjustments.
34692 * @return {Object} The size as an object {width: (the width), height: (the height)}
34694 getViewSize : function()
34697 if(this.el.dom != document.body){
34698 size = this.el.getSize();
34700 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34702 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34703 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34708 * Returns the Element this layout is bound to.
34709 * @return {Roo.Element}
34711 getEl : function(){
34716 * Returns the specified region.
34717 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34718 * @return {Roo.LayoutRegion}
34720 getRegion : function(target){
34721 return this.regions[target.toLowerCase()];
34724 onWindowResize : function(){
34725 if(this.monitorWindowResize){
34732 * Ext JS Library 1.1.1
34733 * Copyright(c) 2006-2007, Ext JS, LLC.
34735 * Originally Released Under LGPL - original licence link has changed is not relivant.
34738 * <script type="text/javascript">
34741 * @class Roo.bootstrap.layout.Border
34742 * @extends Roo.bootstrap.layout.Manager
34743 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34744 * please see: examples/bootstrap/nested.html<br><br>
34746 <b>The container the layout is rendered into can be either the body element or any other element.
34747 If it is not the body element, the container needs to either be an absolute positioned element,
34748 or you will need to add "position:relative" to the css of the container. You will also need to specify
34749 the container size if it is not the body element.</b>
34752 * Create a new Border
34753 * @param {Object} config Configuration options
34755 Roo.bootstrap.layout.Border = function(config){
34756 config = config || {};
34757 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34761 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34762 if(config[region]){
34763 config[region].region = region;
34764 this.addRegion(config[region]);
34770 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34772 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34774 * Creates and adds a new region if it doesn't already exist.
34775 * @param {String} target The target region key (north, south, east, west or center).
34776 * @param {Object} config The regions config object
34777 * @return {BorderLayoutRegion} The new region
34779 addRegion : function(config)
34781 if(!this.regions[config.region]){
34782 var r = this.factory(config);
34783 this.bindRegion(r);
34785 return this.regions[config.region];
34789 bindRegion : function(r){
34790 this.regions[r.config.region] = r;
34792 r.on("visibilitychange", this.layout, this);
34793 r.on("paneladded", this.layout, this);
34794 r.on("panelremoved", this.layout, this);
34795 r.on("invalidated", this.layout, this);
34796 r.on("resized", this.onRegionResized, this);
34797 r.on("collapsed", this.onRegionCollapsed, this);
34798 r.on("expanded", this.onRegionExpanded, this);
34802 * Performs a layout update.
34804 layout : function()
34806 if(this.updating) {
34810 // render all the rebions if they have not been done alreayd?
34811 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34812 if(this.regions[region] && !this.regions[region].bodyEl){
34813 this.regions[region].onRender(this.el)
34817 var size = this.getViewSize();
34818 var w = size.width;
34819 var h = size.height;
34824 //var x = 0, y = 0;
34826 var rs = this.regions;
34827 var north = rs["north"];
34828 var south = rs["south"];
34829 var west = rs["west"];
34830 var east = rs["east"];
34831 var center = rs["center"];
34832 //if(this.hideOnLayout){ // not supported anymore
34833 //c.el.setStyle("display", "none");
34835 if(north && north.isVisible()){
34836 var b = north.getBox();
34837 var m = north.getMargins();
34838 b.width = w - (m.left+m.right);
34841 centerY = b.height + b.y + m.bottom;
34842 centerH -= centerY;
34843 north.updateBox(this.safeBox(b));
34845 if(south && south.isVisible()){
34846 var b = south.getBox();
34847 var m = south.getMargins();
34848 b.width = w - (m.left+m.right);
34850 var totalHeight = (b.height + m.top + m.bottom);
34851 b.y = h - totalHeight + m.top;
34852 centerH -= totalHeight;
34853 south.updateBox(this.safeBox(b));
34855 if(west && west.isVisible()){
34856 var b = west.getBox();
34857 var m = west.getMargins();
34858 b.height = centerH - (m.top+m.bottom);
34860 b.y = centerY + m.top;
34861 var totalWidth = (b.width + m.left + m.right);
34862 centerX += totalWidth;
34863 centerW -= totalWidth;
34864 west.updateBox(this.safeBox(b));
34866 if(east && east.isVisible()){
34867 var b = east.getBox();
34868 var m = east.getMargins();
34869 b.height = centerH - (m.top+m.bottom);
34870 var totalWidth = (b.width + m.left + m.right);
34871 b.x = w - totalWidth + m.left;
34872 b.y = centerY + m.top;
34873 centerW -= totalWidth;
34874 east.updateBox(this.safeBox(b));
34877 var m = center.getMargins();
34879 x: centerX + m.left,
34880 y: centerY + m.top,
34881 width: centerW - (m.left+m.right),
34882 height: centerH - (m.top+m.bottom)
34884 //if(this.hideOnLayout){
34885 //center.el.setStyle("display", "block");
34887 center.updateBox(this.safeBox(centerBox));
34890 this.fireEvent("layout", this);
34894 safeBox : function(box){
34895 box.width = Math.max(0, box.width);
34896 box.height = Math.max(0, box.height);
34901 * Adds a ContentPanel (or subclass) to this layout.
34902 * @param {String} target The target region key (north, south, east, west or center).
34903 * @param {Roo.ContentPanel} panel The panel to add
34904 * @return {Roo.ContentPanel} The added panel
34906 add : function(target, panel){
34908 target = target.toLowerCase();
34909 return this.regions[target].add(panel);
34913 * Remove a ContentPanel (or subclass) to this layout.
34914 * @param {String} target The target region key (north, south, east, west or center).
34915 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34916 * @return {Roo.ContentPanel} The removed panel
34918 remove : function(target, panel){
34919 target = target.toLowerCase();
34920 return this.regions[target].remove(panel);
34924 * Searches all regions for a panel with the specified id
34925 * @param {String} panelId
34926 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34928 findPanel : function(panelId){
34929 var rs = this.regions;
34930 for(var target in rs){
34931 if(typeof rs[target] != "function"){
34932 var p = rs[target].getPanel(panelId);
34942 * Searches all regions for a panel with the specified id and activates (shows) it.
34943 * @param {String/ContentPanel} panelId The panels id or the panel itself
34944 * @return {Roo.ContentPanel} The shown panel or null
34946 showPanel : function(panelId) {
34947 var rs = this.regions;
34948 for(var target in rs){
34949 var r = rs[target];
34950 if(typeof r != "function"){
34951 if(r.hasPanel(panelId)){
34952 return r.showPanel(panelId);
34960 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34961 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34964 restoreState : function(provider){
34966 provider = Roo.state.Manager;
34968 var sm = new Roo.LayoutStateManager();
34969 sm.init(this, provider);
34975 * Adds a xtype elements to the layout.
34979 xtype : 'ContentPanel',
34986 xtype : 'NestedLayoutPanel',
34992 items : [ ... list of content panels or nested layout panels.. ]
34996 * @param {Object} cfg Xtype definition of item to add.
34998 addxtype : function(cfg)
35000 // basically accepts a pannel...
35001 // can accept a layout region..!?!?
35002 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35005 // theory? children can only be panels??
35007 //if (!cfg.xtype.match(/Panel$/)) {
35012 if (typeof(cfg.region) == 'undefined') {
35013 Roo.log("Failed to add Panel, region was not set");
35017 var region = cfg.region;
35023 xitems = cfg.items;
35030 case 'Content': // ContentPanel (el, cfg)
35031 case 'Scroll': // ContentPanel (el, cfg)
35033 cfg.autoCreate = true;
35034 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35036 // var el = this.el.createChild();
35037 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35040 this.add(region, ret);
35044 case 'TreePanel': // our new panel!
35045 cfg.el = this.el.createChild();
35046 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35047 this.add(region, ret);
35052 // create a new Layout (which is a Border Layout...
35054 var clayout = cfg.layout;
35055 clayout.el = this.el.createChild();
35056 clayout.items = clayout.items || [];
35060 // replace this exitems with the clayout ones..
35061 xitems = clayout.items;
35063 // force background off if it's in center...
35064 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35065 cfg.background = false;
35067 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35070 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35071 //console.log('adding nested layout panel ' + cfg.toSource());
35072 this.add(region, ret);
35073 nb = {}; /// find first...
35078 // needs grid and region
35080 //var el = this.getRegion(region).el.createChild();
35082 *var el = this.el.createChild();
35083 // create the grid first...
35084 cfg.grid.container = el;
35085 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35088 if (region == 'center' && this.active ) {
35089 cfg.background = false;
35092 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35094 this.add(region, ret);
35096 if (cfg.background) {
35097 // render grid on panel activation (if panel background)
35098 ret.on('activate', function(gp) {
35099 if (!gp.grid.rendered) {
35100 // gp.grid.render(el);
35104 // cfg.grid.render(el);
35110 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35111 // it was the old xcomponent building that caused this before.
35112 // espeically if border is the top element in the tree.
35122 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35124 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35125 this.add(region, ret);
35129 throw "Can not add '" + cfg.xtype + "' to Border";
35135 this.beginUpdate();
35139 Roo.each(xitems, function(i) {
35140 region = nb && i.region ? i.region : false;
35142 var add = ret.addxtype(i);
35145 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35146 if (!i.background) {
35147 abn[region] = nb[region] ;
35154 // make the last non-background panel active..
35155 //if (nb) { Roo.log(abn); }
35158 for(var r in abn) {
35159 region = this.getRegion(r);
35161 // tried using nb[r], but it does not work..
35163 region.showPanel(abn[r]);
35174 factory : function(cfg)
35177 var validRegions = Roo.bootstrap.layout.Border.regions;
35179 var target = cfg.region;
35182 var r = Roo.bootstrap.layout;
35186 return new r.North(cfg);
35188 return new r.South(cfg);
35190 return new r.East(cfg);
35192 return new r.West(cfg);
35194 return new r.Center(cfg);
35196 throw 'Layout region "'+target+'" not supported.';
35203 * Ext JS Library 1.1.1
35204 * Copyright(c) 2006-2007, Ext JS, LLC.
35206 * Originally Released Under LGPL - original licence link has changed is not relivant.
35209 * <script type="text/javascript">
35213 * @class Roo.bootstrap.layout.Basic
35214 * @extends Roo.util.Observable
35215 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35216 * and does not have a titlebar, tabs or any other features. All it does is size and position
35217 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35218 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35219 * @cfg {string} region the region that it inhabits..
35220 * @cfg {bool} skipConfig skip config?
35224 Roo.bootstrap.layout.Basic = function(config){
35226 this.mgr = config.mgr;
35228 this.position = config.region;
35230 var skipConfig = config.skipConfig;
35234 * @scope Roo.BasicLayoutRegion
35238 * @event beforeremove
35239 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35240 * @param {Roo.LayoutRegion} this
35241 * @param {Roo.ContentPanel} panel The panel
35242 * @param {Object} e The cancel event object
35244 "beforeremove" : true,
35246 * @event invalidated
35247 * Fires when the layout for this region is changed.
35248 * @param {Roo.LayoutRegion} this
35250 "invalidated" : true,
35252 * @event visibilitychange
35253 * Fires when this region is shown or hidden
35254 * @param {Roo.LayoutRegion} this
35255 * @param {Boolean} visibility true or false
35257 "visibilitychange" : true,
35259 * @event paneladded
35260 * Fires when a panel is added.
35261 * @param {Roo.LayoutRegion} this
35262 * @param {Roo.ContentPanel} panel The panel
35264 "paneladded" : true,
35266 * @event panelremoved
35267 * Fires when a panel is removed.
35268 * @param {Roo.LayoutRegion} this
35269 * @param {Roo.ContentPanel} panel The panel
35271 "panelremoved" : true,
35273 * @event beforecollapse
35274 * Fires when this region before collapse.
35275 * @param {Roo.LayoutRegion} this
35277 "beforecollapse" : true,
35280 * Fires when this region is collapsed.
35281 * @param {Roo.LayoutRegion} this
35283 "collapsed" : true,
35286 * Fires when this region is expanded.
35287 * @param {Roo.LayoutRegion} this
35292 * Fires when this region is slid into view.
35293 * @param {Roo.LayoutRegion} this
35295 "slideshow" : true,
35298 * Fires when this region slides out of view.
35299 * @param {Roo.LayoutRegion} this
35301 "slidehide" : true,
35303 * @event panelactivated
35304 * Fires when a panel is activated.
35305 * @param {Roo.LayoutRegion} this
35306 * @param {Roo.ContentPanel} panel The activated panel
35308 "panelactivated" : true,
35311 * Fires when the user resizes this region.
35312 * @param {Roo.LayoutRegion} this
35313 * @param {Number} newSize The new size (width for east/west, height for north/south)
35317 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35318 this.panels = new Roo.util.MixedCollection();
35319 this.panels.getKey = this.getPanelId.createDelegate(this);
35321 this.activePanel = null;
35322 // ensure listeners are added...
35324 if (config.listeners || config.events) {
35325 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35326 listeners : config.listeners || {},
35327 events : config.events || {}
35331 if(skipConfig !== true){
35332 this.applyConfig(config);
35336 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35338 getPanelId : function(p){
35342 applyConfig : function(config){
35343 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35344 this.config = config;
35349 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35350 * the width, for horizontal (north, south) the height.
35351 * @param {Number} newSize The new width or height
35353 resizeTo : function(newSize){
35354 var el = this.el ? this.el :
35355 (this.activePanel ? this.activePanel.getEl() : null);
35357 switch(this.position){
35360 el.setWidth(newSize);
35361 this.fireEvent("resized", this, newSize);
35365 el.setHeight(newSize);
35366 this.fireEvent("resized", this, newSize);
35372 getBox : function(){
35373 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35376 getMargins : function(){
35377 return this.margins;
35380 updateBox : function(box){
35382 var el = this.activePanel.getEl();
35383 el.dom.style.left = box.x + "px";
35384 el.dom.style.top = box.y + "px";
35385 this.activePanel.setSize(box.width, box.height);
35389 * Returns the container element for this region.
35390 * @return {Roo.Element}
35392 getEl : function(){
35393 return this.activePanel;
35397 * Returns true if this region is currently visible.
35398 * @return {Boolean}
35400 isVisible : function(){
35401 return this.activePanel ? true : false;
35404 setActivePanel : function(panel){
35405 panel = this.getPanel(panel);
35406 if(this.activePanel && this.activePanel != panel){
35407 this.activePanel.setActiveState(false);
35408 this.activePanel.getEl().setLeftTop(-10000,-10000);
35410 this.activePanel = panel;
35411 panel.setActiveState(true);
35413 panel.setSize(this.box.width, this.box.height);
35415 this.fireEvent("panelactivated", this, panel);
35416 this.fireEvent("invalidated");
35420 * Show the specified panel.
35421 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35422 * @return {Roo.ContentPanel} The shown panel or null
35424 showPanel : function(panel){
35425 panel = this.getPanel(panel);
35427 this.setActivePanel(panel);
35433 * Get the active panel for this region.
35434 * @return {Roo.ContentPanel} The active panel or null
35436 getActivePanel : function(){
35437 return this.activePanel;
35441 * Add the passed ContentPanel(s)
35442 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35443 * @return {Roo.ContentPanel} The panel added (if only one was added)
35445 add : function(panel){
35446 if(arguments.length > 1){
35447 for(var i = 0, len = arguments.length; i < len; i++) {
35448 this.add(arguments[i]);
35452 if(this.hasPanel(panel)){
35453 this.showPanel(panel);
35456 var el = panel.getEl();
35457 if(el.dom.parentNode != this.mgr.el.dom){
35458 this.mgr.el.dom.appendChild(el.dom);
35460 if(panel.setRegion){
35461 panel.setRegion(this);
35463 this.panels.add(panel);
35464 el.setStyle("position", "absolute");
35465 if(!panel.background){
35466 this.setActivePanel(panel);
35467 if(this.config.initialSize && this.panels.getCount()==1){
35468 this.resizeTo(this.config.initialSize);
35471 this.fireEvent("paneladded", this, panel);
35476 * Returns true if the panel is in this region.
35477 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35478 * @return {Boolean}
35480 hasPanel : function(panel){
35481 if(typeof panel == "object"){ // must be panel obj
35482 panel = panel.getId();
35484 return this.getPanel(panel) ? true : false;
35488 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35489 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35490 * @param {Boolean} preservePanel Overrides the config preservePanel option
35491 * @return {Roo.ContentPanel} The panel that was removed
35493 remove : function(panel, preservePanel){
35494 panel = this.getPanel(panel);
35499 this.fireEvent("beforeremove", this, panel, e);
35500 if(e.cancel === true){
35503 var panelId = panel.getId();
35504 this.panels.removeKey(panelId);
35509 * Returns the panel specified or null if it's not in this region.
35510 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35511 * @return {Roo.ContentPanel}
35513 getPanel : function(id){
35514 if(typeof id == "object"){ // must be panel obj
35517 return this.panels.get(id);
35521 * Returns this regions position (north/south/east/west/center).
35524 getPosition: function(){
35525 return this.position;
35529 * Ext JS Library 1.1.1
35530 * Copyright(c) 2006-2007, Ext JS, LLC.
35532 * Originally Released Under LGPL - original licence link has changed is not relivant.
35535 * <script type="text/javascript">
35539 * @class Roo.bootstrap.layout.Region
35540 * @extends Roo.bootstrap.layout.Basic
35541 * This class represents a region in a layout manager.
35543 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35544 * @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})
35545 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35546 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35547 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35548 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35549 * @cfg {String} title The title for the region (overrides panel titles)
35550 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35551 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35552 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35553 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35554 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35555 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35556 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35557 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35558 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35559 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35561 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35562 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35563 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35564 * @cfg {Number} width For East/West panels
35565 * @cfg {Number} height For North/South panels
35566 * @cfg {Boolean} split To show the splitter
35567 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35569 * @cfg {string} cls Extra CSS classes to add to region
35571 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35572 * @cfg {string} region the region that it inhabits..
35575 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35576 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35578 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35579 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35580 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35582 Roo.bootstrap.layout.Region = function(config)
35584 this.applyConfig(config);
35586 var mgr = config.mgr;
35587 var pos = config.region;
35588 config.skipConfig = true;
35589 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35592 this.onRender(mgr.el);
35595 this.visible = true;
35596 this.collapsed = false;
35597 this.unrendered_panels = [];
35600 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35602 position: '', // set by wrapper (eg. north/south etc..)
35603 unrendered_panels : null, // unrendered panels.
35604 createBody : function(){
35605 /** This region's body element
35606 * @type Roo.Element */
35607 this.bodyEl = this.el.createChild({
35609 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35613 onRender: function(ctr, pos)
35615 var dh = Roo.DomHelper;
35616 /** This region's container element
35617 * @type Roo.Element */
35618 this.el = dh.append(ctr.dom, {
35620 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35622 /** This region's title element
35623 * @type Roo.Element */
35625 this.titleEl = dh.append(this.el.dom,
35628 unselectable: "on",
35629 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35631 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35632 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35635 this.titleEl.enableDisplayMode();
35636 /** This region's title text element
35637 * @type HTMLElement */
35638 this.titleTextEl = this.titleEl.dom.firstChild;
35639 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35641 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35642 this.closeBtn.enableDisplayMode();
35643 this.closeBtn.on("click", this.closeClicked, this);
35644 this.closeBtn.hide();
35646 this.createBody(this.config);
35647 if(this.config.hideWhenEmpty){
35649 this.on("paneladded", this.validateVisibility, this);
35650 this.on("panelremoved", this.validateVisibility, this);
35652 if(this.autoScroll){
35653 this.bodyEl.setStyle("overflow", "auto");
35655 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35657 //if(c.titlebar !== false){
35658 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35659 this.titleEl.hide();
35661 this.titleEl.show();
35662 if(this.config.title){
35663 this.titleTextEl.innerHTML = this.config.title;
35667 if(this.config.collapsed){
35668 this.collapse(true);
35670 if(this.config.hidden){
35674 if (this.unrendered_panels && this.unrendered_panels.length) {
35675 for (var i =0;i< this.unrendered_panels.length; i++) {
35676 this.add(this.unrendered_panels[i]);
35678 this.unrendered_panels = null;
35684 applyConfig : function(c)
35687 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35688 var dh = Roo.DomHelper;
35689 if(c.titlebar !== false){
35690 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35691 this.collapseBtn.on("click", this.collapse, this);
35692 this.collapseBtn.enableDisplayMode();
35694 if(c.showPin === true || this.showPin){
35695 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35696 this.stickBtn.enableDisplayMode();
35697 this.stickBtn.on("click", this.expand, this);
35698 this.stickBtn.hide();
35703 /** This region's collapsed element
35704 * @type Roo.Element */
35707 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35708 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35711 if(c.floatable !== false){
35712 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35713 this.collapsedEl.on("click", this.collapseClick, this);
35716 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35717 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35718 id: "message", unselectable: "on", style:{"float":"left"}});
35719 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35721 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35722 this.expandBtn.on("click", this.expand, this);
35726 if(this.collapseBtn){
35727 this.collapseBtn.setVisible(c.collapsible == true);
35730 this.cmargins = c.cmargins || this.cmargins ||
35731 (this.position == "west" || this.position == "east" ?
35732 {top: 0, left: 2, right:2, bottom: 0} :
35733 {top: 2, left: 0, right:0, bottom: 2});
35735 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35738 this.bottomTabs = c.tabPosition != "top";
35740 this.autoScroll = c.autoScroll || false;
35745 this.duration = c.duration || .30;
35746 this.slideDuration = c.slideDuration || .45;
35751 * Returns true if this region is currently visible.
35752 * @return {Boolean}
35754 isVisible : function(){
35755 return this.visible;
35759 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35760 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35762 //setCollapsedTitle : function(title){
35763 // title = title || " ";
35764 // if(this.collapsedTitleTextEl){
35765 // this.collapsedTitleTextEl.innerHTML = title;
35769 getBox : function(){
35771 // if(!this.collapsed){
35772 b = this.el.getBox(false, true);
35774 // b = this.collapsedEl.getBox(false, true);
35779 getMargins : function(){
35780 return this.margins;
35781 //return this.collapsed ? this.cmargins : this.margins;
35784 highlight : function(){
35785 this.el.addClass("x-layout-panel-dragover");
35788 unhighlight : function(){
35789 this.el.removeClass("x-layout-panel-dragover");
35792 updateBox : function(box)
35794 if (!this.bodyEl) {
35795 return; // not rendered yet..
35799 if(!this.collapsed){
35800 this.el.dom.style.left = box.x + "px";
35801 this.el.dom.style.top = box.y + "px";
35802 this.updateBody(box.width, box.height);
35804 this.collapsedEl.dom.style.left = box.x + "px";
35805 this.collapsedEl.dom.style.top = box.y + "px";
35806 this.collapsedEl.setSize(box.width, box.height);
35809 this.tabs.autoSizeTabs();
35813 updateBody : function(w, h)
35816 this.el.setWidth(w);
35817 w -= this.el.getBorderWidth("rl");
35818 if(this.config.adjustments){
35819 w += this.config.adjustments[0];
35822 if(h !== null && h > 0){
35823 this.el.setHeight(h);
35824 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35825 h -= this.el.getBorderWidth("tb");
35826 if(this.config.adjustments){
35827 h += this.config.adjustments[1];
35829 this.bodyEl.setHeight(h);
35831 h = this.tabs.syncHeight(h);
35834 if(this.panelSize){
35835 w = w !== null ? w : this.panelSize.width;
35836 h = h !== null ? h : this.panelSize.height;
35838 if(this.activePanel){
35839 var el = this.activePanel.getEl();
35840 w = w !== null ? w : el.getWidth();
35841 h = h !== null ? h : el.getHeight();
35842 this.panelSize = {width: w, height: h};
35843 this.activePanel.setSize(w, h);
35845 if(Roo.isIE && this.tabs){
35846 this.tabs.el.repaint();
35851 * Returns the container element for this region.
35852 * @return {Roo.Element}
35854 getEl : function(){
35859 * Hides this region.
35862 //if(!this.collapsed){
35863 this.el.dom.style.left = "-2000px";
35866 // this.collapsedEl.dom.style.left = "-2000px";
35867 // this.collapsedEl.hide();
35869 this.visible = false;
35870 this.fireEvent("visibilitychange", this, false);
35874 * Shows this region if it was previously hidden.
35877 //if(!this.collapsed){
35880 // this.collapsedEl.show();
35882 this.visible = true;
35883 this.fireEvent("visibilitychange", this, true);
35886 closeClicked : function(){
35887 if(this.activePanel){
35888 this.remove(this.activePanel);
35892 collapseClick : function(e){
35894 e.stopPropagation();
35897 e.stopPropagation();
35903 * Collapses this region.
35904 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35907 collapse : function(skipAnim, skipCheck = false){
35908 if(this.collapsed) {
35912 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35914 this.collapsed = true;
35916 this.split.el.hide();
35918 if(this.config.animate && skipAnim !== true){
35919 this.fireEvent("invalidated", this);
35920 this.animateCollapse();
35922 this.el.setLocation(-20000,-20000);
35924 this.collapsedEl.show();
35925 this.fireEvent("collapsed", this);
35926 this.fireEvent("invalidated", this);
35932 animateCollapse : function(){
35937 * Expands this region if it was previously collapsed.
35938 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35939 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35942 expand : function(e, skipAnim){
35944 e.stopPropagation();
35946 if(!this.collapsed || this.el.hasActiveFx()) {
35950 this.afterSlideIn();
35953 this.collapsed = false;
35954 if(this.config.animate && skipAnim !== true){
35955 this.animateExpand();
35959 this.split.el.show();
35961 this.collapsedEl.setLocation(-2000,-2000);
35962 this.collapsedEl.hide();
35963 this.fireEvent("invalidated", this);
35964 this.fireEvent("expanded", this);
35968 animateExpand : function(){
35972 initTabs : function()
35974 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35976 var ts = new Roo.bootstrap.panel.Tabs({
35977 el: this.bodyEl.dom,
35978 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35979 disableTooltips: this.config.disableTabTips,
35980 toolbar : this.config.toolbar
35983 if(this.config.hideTabs){
35984 ts.stripWrap.setDisplayed(false);
35987 ts.resizeTabs = this.config.resizeTabs === true;
35988 ts.minTabWidth = this.config.minTabWidth || 40;
35989 ts.maxTabWidth = this.config.maxTabWidth || 250;
35990 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35991 ts.monitorResize = false;
35992 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35993 ts.bodyEl.addClass('roo-layout-tabs-body');
35994 this.panels.each(this.initPanelAsTab, this);
35997 initPanelAsTab : function(panel){
35998 var ti = this.tabs.addTab(
36002 this.config.closeOnTab && panel.isClosable(),
36005 if(panel.tabTip !== undefined){
36006 ti.setTooltip(panel.tabTip);
36008 ti.on("activate", function(){
36009 this.setActivePanel(panel);
36012 if(this.config.closeOnTab){
36013 ti.on("beforeclose", function(t, e){
36015 this.remove(panel);
36019 panel.tabItem = ti;
36024 updatePanelTitle : function(panel, title)
36026 if(this.activePanel == panel){
36027 this.updateTitle(title);
36030 var ti = this.tabs.getTab(panel.getEl().id);
36032 if(panel.tabTip !== undefined){
36033 ti.setTooltip(panel.tabTip);
36038 updateTitle : function(title){
36039 if(this.titleTextEl && !this.config.title){
36040 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36044 setActivePanel : function(panel)
36046 panel = this.getPanel(panel);
36047 if(this.activePanel && this.activePanel != panel){
36048 if(this.activePanel.setActiveState(false) === false){
36052 this.activePanel = panel;
36053 panel.setActiveState(true);
36054 if(this.panelSize){
36055 panel.setSize(this.panelSize.width, this.panelSize.height);
36058 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36060 this.updateTitle(panel.getTitle());
36062 this.fireEvent("invalidated", this);
36064 this.fireEvent("panelactivated", this, panel);
36068 * Shows the specified panel.
36069 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36070 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36072 showPanel : function(panel)
36074 panel = this.getPanel(panel);
36077 var tab = this.tabs.getTab(panel.getEl().id);
36078 if(tab.isHidden()){
36079 this.tabs.unhideTab(tab.id);
36083 this.setActivePanel(panel);
36090 * Get the active panel for this region.
36091 * @return {Roo.ContentPanel} The active panel or null
36093 getActivePanel : function(){
36094 return this.activePanel;
36097 validateVisibility : function(){
36098 if(this.panels.getCount() < 1){
36099 this.updateTitle(" ");
36100 this.closeBtn.hide();
36103 if(!this.isVisible()){
36110 * Adds the passed ContentPanel(s) to this region.
36111 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36112 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36114 add : function(panel)
36116 if(arguments.length > 1){
36117 for(var i = 0, len = arguments.length; i < len; i++) {
36118 this.add(arguments[i]);
36123 // if we have not been rendered yet, then we can not really do much of this..
36124 if (!this.bodyEl) {
36125 this.unrendered_panels.push(panel);
36132 if(this.hasPanel(panel)){
36133 this.showPanel(panel);
36136 panel.setRegion(this);
36137 this.panels.add(panel);
36138 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36139 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36140 // and hide them... ???
36141 this.bodyEl.dom.appendChild(panel.getEl().dom);
36142 if(panel.background !== true){
36143 this.setActivePanel(panel);
36145 this.fireEvent("paneladded", this, panel);
36152 this.initPanelAsTab(panel);
36156 if(panel.background !== true){
36157 this.tabs.activate(panel.getEl().id);
36159 this.fireEvent("paneladded", this, panel);
36164 * Hides the tab for the specified panel.
36165 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36167 hidePanel : function(panel){
36168 if(this.tabs && (panel = this.getPanel(panel))){
36169 this.tabs.hideTab(panel.getEl().id);
36174 * Unhides the tab for a previously hidden panel.
36175 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36177 unhidePanel : function(panel){
36178 if(this.tabs && (panel = this.getPanel(panel))){
36179 this.tabs.unhideTab(panel.getEl().id);
36183 clearPanels : function(){
36184 while(this.panels.getCount() > 0){
36185 this.remove(this.panels.first());
36190 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36191 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36192 * @param {Boolean} preservePanel Overrides the config preservePanel option
36193 * @return {Roo.ContentPanel} The panel that was removed
36195 remove : function(panel, preservePanel)
36197 panel = this.getPanel(panel);
36202 this.fireEvent("beforeremove", this, panel, e);
36203 if(e.cancel === true){
36206 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36207 var panelId = panel.getId();
36208 this.panels.removeKey(panelId);
36210 document.body.appendChild(panel.getEl().dom);
36213 this.tabs.removeTab(panel.getEl().id);
36214 }else if (!preservePanel){
36215 this.bodyEl.dom.removeChild(panel.getEl().dom);
36217 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36218 var p = this.panels.first();
36219 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36220 tempEl.appendChild(p.getEl().dom);
36221 this.bodyEl.update("");
36222 this.bodyEl.dom.appendChild(p.getEl().dom);
36224 this.updateTitle(p.getTitle());
36226 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36227 this.setActivePanel(p);
36229 panel.setRegion(null);
36230 if(this.activePanel == panel){
36231 this.activePanel = null;
36233 if(this.config.autoDestroy !== false && preservePanel !== true){
36234 try{panel.destroy();}catch(e){}
36236 this.fireEvent("panelremoved", this, panel);
36241 * Returns the TabPanel component used by this region
36242 * @return {Roo.TabPanel}
36244 getTabs : function(){
36248 createTool : function(parentEl, className){
36249 var btn = Roo.DomHelper.append(parentEl, {
36251 cls: "x-layout-tools-button",
36254 cls: "roo-layout-tools-button-inner " + className,
36258 btn.addClassOnOver("roo-layout-tools-button-over");
36263 * Ext JS Library 1.1.1
36264 * Copyright(c) 2006-2007, Ext JS, LLC.
36266 * Originally Released Under LGPL - original licence link has changed is not relivant.
36269 * <script type="text/javascript">
36275 * @class Roo.SplitLayoutRegion
36276 * @extends Roo.LayoutRegion
36277 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36279 Roo.bootstrap.layout.Split = function(config){
36280 this.cursor = config.cursor;
36281 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36284 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36286 splitTip : "Drag to resize.",
36287 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36288 useSplitTips : false,
36290 applyConfig : function(config){
36291 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36294 onRender : function(ctr,pos) {
36296 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36297 if(!this.config.split){
36302 var splitEl = Roo.DomHelper.append(ctr.dom, {
36304 id: this.el.id + "-split",
36305 cls: "roo-layout-split roo-layout-split-"+this.position,
36308 /** The SplitBar for this region
36309 * @type Roo.SplitBar */
36310 // does not exist yet...
36311 Roo.log([this.position, this.orientation]);
36313 this.split = new Roo.bootstrap.SplitBar({
36314 dragElement : splitEl,
36315 resizingElement: this.el,
36316 orientation : this.orientation
36319 this.split.on("moved", this.onSplitMove, this);
36320 this.split.useShim = this.config.useShim === true;
36321 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36322 if(this.useSplitTips){
36323 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36325 //if(config.collapsible){
36326 // this.split.el.on("dblclick", this.collapse, this);
36329 if(typeof this.config.minSize != "undefined"){
36330 this.split.minSize = this.config.minSize;
36332 if(typeof this.config.maxSize != "undefined"){
36333 this.split.maxSize = this.config.maxSize;
36335 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36336 this.hideSplitter();
36341 getHMaxSize : function(){
36342 var cmax = this.config.maxSize || 10000;
36343 var center = this.mgr.getRegion("center");
36344 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36347 getVMaxSize : function(){
36348 var cmax = this.config.maxSize || 10000;
36349 var center = this.mgr.getRegion("center");
36350 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36353 onSplitMove : function(split, newSize){
36354 this.fireEvent("resized", this, newSize);
36358 * Returns the {@link Roo.SplitBar} for this region.
36359 * @return {Roo.SplitBar}
36361 getSplitBar : function(){
36366 this.hideSplitter();
36367 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36370 hideSplitter : function(){
36372 this.split.el.setLocation(-2000,-2000);
36373 this.split.el.hide();
36379 this.split.el.show();
36381 Roo.bootstrap.layout.Split.superclass.show.call(this);
36384 beforeSlide: function(){
36385 if(Roo.isGecko){// firefox overflow auto bug workaround
36386 this.bodyEl.clip();
36388 this.tabs.bodyEl.clip();
36390 if(this.activePanel){
36391 this.activePanel.getEl().clip();
36393 if(this.activePanel.beforeSlide){
36394 this.activePanel.beforeSlide();
36400 afterSlide : function(){
36401 if(Roo.isGecko){// firefox overflow auto bug workaround
36402 this.bodyEl.unclip();
36404 this.tabs.bodyEl.unclip();
36406 if(this.activePanel){
36407 this.activePanel.getEl().unclip();
36408 if(this.activePanel.afterSlide){
36409 this.activePanel.afterSlide();
36415 initAutoHide : function(){
36416 if(this.autoHide !== false){
36417 if(!this.autoHideHd){
36418 var st = new Roo.util.DelayedTask(this.slideIn, this);
36419 this.autoHideHd = {
36420 "mouseout": function(e){
36421 if(!e.within(this.el, true)){
36425 "mouseover" : function(e){
36431 this.el.on(this.autoHideHd);
36435 clearAutoHide : function(){
36436 if(this.autoHide !== false){
36437 this.el.un("mouseout", this.autoHideHd.mouseout);
36438 this.el.un("mouseover", this.autoHideHd.mouseover);
36442 clearMonitor : function(){
36443 Roo.get(document).un("click", this.slideInIf, this);
36446 // these names are backwards but not changed for compat
36447 slideOut : function(){
36448 if(this.isSlid || this.el.hasActiveFx()){
36451 this.isSlid = true;
36452 if(this.collapseBtn){
36453 this.collapseBtn.hide();
36455 this.closeBtnState = this.closeBtn.getStyle('display');
36456 this.closeBtn.hide();
36458 this.stickBtn.show();
36461 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36462 this.beforeSlide();
36463 this.el.setStyle("z-index", 10001);
36464 this.el.slideIn(this.getSlideAnchor(), {
36465 callback: function(){
36467 this.initAutoHide();
36468 Roo.get(document).on("click", this.slideInIf, this);
36469 this.fireEvent("slideshow", this);
36476 afterSlideIn : function(){
36477 this.clearAutoHide();
36478 this.isSlid = false;
36479 this.clearMonitor();
36480 this.el.setStyle("z-index", "");
36481 if(this.collapseBtn){
36482 this.collapseBtn.show();
36484 this.closeBtn.setStyle('display', this.closeBtnState);
36486 this.stickBtn.hide();
36488 this.fireEvent("slidehide", this);
36491 slideIn : function(cb){
36492 if(!this.isSlid || this.el.hasActiveFx()){
36496 this.isSlid = false;
36497 this.beforeSlide();
36498 this.el.slideOut(this.getSlideAnchor(), {
36499 callback: function(){
36500 this.el.setLeftTop(-10000, -10000);
36502 this.afterSlideIn();
36510 slideInIf : function(e){
36511 if(!e.within(this.el)){
36516 animateCollapse : function(){
36517 this.beforeSlide();
36518 this.el.setStyle("z-index", 20000);
36519 var anchor = this.getSlideAnchor();
36520 this.el.slideOut(anchor, {
36521 callback : function(){
36522 this.el.setStyle("z-index", "");
36523 this.collapsedEl.slideIn(anchor, {duration:.3});
36525 this.el.setLocation(-10000,-10000);
36527 this.fireEvent("collapsed", this);
36534 animateExpand : function(){
36535 this.beforeSlide();
36536 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36537 this.el.setStyle("z-index", 20000);
36538 this.collapsedEl.hide({
36541 this.el.slideIn(this.getSlideAnchor(), {
36542 callback : function(){
36543 this.el.setStyle("z-index", "");
36546 this.split.el.show();
36548 this.fireEvent("invalidated", this);
36549 this.fireEvent("expanded", this);
36577 getAnchor : function(){
36578 return this.anchors[this.position];
36581 getCollapseAnchor : function(){
36582 return this.canchors[this.position];
36585 getSlideAnchor : function(){
36586 return this.sanchors[this.position];
36589 getAlignAdj : function(){
36590 var cm = this.cmargins;
36591 switch(this.position){
36607 getExpandAdj : function(){
36608 var c = this.collapsedEl, cm = this.cmargins;
36609 switch(this.position){
36611 return [-(cm.right+c.getWidth()+cm.left), 0];
36614 return [cm.right+c.getWidth()+cm.left, 0];
36617 return [0, -(cm.top+cm.bottom+c.getHeight())];
36620 return [0, cm.top+cm.bottom+c.getHeight()];
36626 * Ext JS Library 1.1.1
36627 * Copyright(c) 2006-2007, Ext JS, LLC.
36629 * Originally Released Under LGPL - original licence link has changed is not relivant.
36632 * <script type="text/javascript">
36635 * These classes are private internal classes
36637 Roo.bootstrap.layout.Center = function(config){
36638 config.region = "center";
36639 Roo.bootstrap.layout.Region.call(this, config);
36640 this.visible = true;
36641 this.minWidth = config.minWidth || 20;
36642 this.minHeight = config.minHeight || 20;
36645 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36647 // center panel can't be hidden
36651 // center panel can't be hidden
36654 getMinWidth: function(){
36655 return this.minWidth;
36658 getMinHeight: function(){
36659 return this.minHeight;
36672 Roo.bootstrap.layout.North = function(config)
36674 config.region = 'north';
36675 config.cursor = 'n-resize';
36677 Roo.bootstrap.layout.Split.call(this, config);
36681 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36682 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36683 this.split.el.addClass("roo-layout-split-v");
36685 var size = config.initialSize || config.height;
36686 if(typeof size != "undefined"){
36687 this.el.setHeight(size);
36690 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36692 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36696 getBox : function(){
36697 if(this.collapsed){
36698 return this.collapsedEl.getBox();
36700 var box = this.el.getBox();
36702 box.height += this.split.el.getHeight();
36707 updateBox : function(box){
36708 if(this.split && !this.collapsed){
36709 box.height -= this.split.el.getHeight();
36710 this.split.el.setLeft(box.x);
36711 this.split.el.setTop(box.y+box.height);
36712 this.split.el.setWidth(box.width);
36714 if(this.collapsed){
36715 this.updateBody(box.width, null);
36717 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36725 Roo.bootstrap.layout.South = function(config){
36726 config.region = 'south';
36727 config.cursor = 's-resize';
36728 Roo.bootstrap.layout.Split.call(this, config);
36730 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36731 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36732 this.split.el.addClass("roo-layout-split-v");
36734 var size = config.initialSize || config.height;
36735 if(typeof size != "undefined"){
36736 this.el.setHeight(size);
36740 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36741 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36742 getBox : function(){
36743 if(this.collapsed){
36744 return this.collapsedEl.getBox();
36746 var box = this.el.getBox();
36748 var sh = this.split.el.getHeight();
36755 updateBox : function(box){
36756 if(this.split && !this.collapsed){
36757 var sh = this.split.el.getHeight();
36760 this.split.el.setLeft(box.x);
36761 this.split.el.setTop(box.y-sh);
36762 this.split.el.setWidth(box.width);
36764 if(this.collapsed){
36765 this.updateBody(box.width, null);
36767 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36771 Roo.bootstrap.layout.East = function(config){
36772 config.region = "east";
36773 config.cursor = "e-resize";
36774 Roo.bootstrap.layout.Split.call(this, config);
36776 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36777 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36778 this.split.el.addClass("roo-layout-split-h");
36780 var size = config.initialSize || config.width;
36781 if(typeof size != "undefined"){
36782 this.el.setWidth(size);
36785 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36786 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36787 getBox : function(){
36788 if(this.collapsed){
36789 return this.collapsedEl.getBox();
36791 var box = this.el.getBox();
36793 var sw = this.split.el.getWidth();
36800 updateBox : function(box){
36801 if(this.split && !this.collapsed){
36802 var sw = this.split.el.getWidth();
36804 this.split.el.setLeft(box.x);
36805 this.split.el.setTop(box.y);
36806 this.split.el.setHeight(box.height);
36809 if(this.collapsed){
36810 this.updateBody(null, box.height);
36812 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36816 Roo.bootstrap.layout.West = function(config){
36817 config.region = "west";
36818 config.cursor = "w-resize";
36820 Roo.bootstrap.layout.Split.call(this, config);
36822 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36823 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36824 this.split.el.addClass("roo-layout-split-h");
36828 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36829 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36831 onRender: function(ctr, pos)
36833 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36834 var size = this.config.initialSize || this.config.width;
36835 if(typeof size != "undefined"){
36836 this.el.setWidth(size);
36840 getBox : function(){
36841 if(this.collapsed){
36842 return this.collapsedEl.getBox();
36844 var box = this.el.getBox();
36846 box.width += this.split.el.getWidth();
36851 updateBox : function(box){
36852 if(this.split && !this.collapsed){
36853 var sw = this.split.el.getWidth();
36855 this.split.el.setLeft(box.x+box.width);
36856 this.split.el.setTop(box.y);
36857 this.split.el.setHeight(box.height);
36859 if(this.collapsed){
36860 this.updateBody(null, box.height);
36862 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36865 Roo.namespace("Roo.bootstrap.panel");/*
36867 * Ext JS Library 1.1.1
36868 * Copyright(c) 2006-2007, Ext JS, LLC.
36870 * Originally Released Under LGPL - original licence link has changed is not relivant.
36873 * <script type="text/javascript">
36876 * @class Roo.ContentPanel
36877 * @extends Roo.util.Observable
36878 * A basic ContentPanel element.
36879 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36880 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36881 * @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
36882 * @cfg {Boolean} closable True if the panel can be closed/removed
36883 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36884 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36885 * @cfg {Toolbar} toolbar A toolbar for this panel
36886 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36887 * @cfg {String} title The title for this panel
36888 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36889 * @cfg {String} url Calls {@link #setUrl} with this value
36890 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36891 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36892 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36893 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36894 * @cfg {Boolean} badges render the badges
36897 * Create a new ContentPanel.
36898 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36899 * @param {String/Object} config A string to set only the title or a config object
36900 * @param {String} content (optional) Set the HTML content for this panel
36901 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36903 Roo.bootstrap.panel.Content = function( config){
36905 this.tpl = config.tpl || false;
36907 var el = config.el;
36908 var content = config.content;
36910 if(config.autoCreate){ // xtype is available if this is called from factory
36913 this.el = Roo.get(el);
36914 if(!this.el && config && config.autoCreate){
36915 if(typeof config.autoCreate == "object"){
36916 if(!config.autoCreate.id){
36917 config.autoCreate.id = config.id||el;
36919 this.el = Roo.DomHelper.append(document.body,
36920 config.autoCreate, true);
36922 var elcfg = { tag: "div",
36923 cls: "roo-layout-inactive-content",
36927 elcfg.html = config.html;
36931 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36934 this.closable = false;
36935 this.loaded = false;
36936 this.active = false;
36939 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36941 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36943 this.wrapEl = this.el; //this.el.wrap();
36945 if (config.toolbar.items) {
36946 ti = config.toolbar.items ;
36947 delete config.toolbar.items ;
36951 this.toolbar.render(this.wrapEl, 'before');
36952 for(var i =0;i < ti.length;i++) {
36953 // Roo.log(['add child', items[i]]);
36954 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36956 this.toolbar.items = nitems;
36957 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36958 delete config.toolbar;
36962 // xtype created footer. - not sure if will work as we normally have to render first..
36963 if (this.footer && !this.footer.el && this.footer.xtype) {
36964 if (!this.wrapEl) {
36965 this.wrapEl = this.el.wrap();
36968 this.footer.container = this.wrapEl.createChild();
36970 this.footer = Roo.factory(this.footer, Roo);
36975 if(typeof config == "string"){
36976 this.title = config;
36978 Roo.apply(this, config);
36982 this.resizeEl = Roo.get(this.resizeEl, true);
36984 this.resizeEl = this.el;
36986 // handle view.xtype
36994 * Fires when this panel is activated.
36995 * @param {Roo.ContentPanel} this
36999 * @event deactivate
37000 * Fires when this panel is activated.
37001 * @param {Roo.ContentPanel} this
37003 "deactivate" : true,
37007 * Fires when this panel is resized if fitToFrame is true.
37008 * @param {Roo.ContentPanel} this
37009 * @param {Number} width The width after any component adjustments
37010 * @param {Number} height The height after any component adjustments
37016 * Fires when this tab is created
37017 * @param {Roo.ContentPanel} this
37028 if(this.autoScroll){
37029 this.resizeEl.setStyle("overflow", "auto");
37031 // fix randome scrolling
37032 //this.el.on('scroll', function() {
37033 // Roo.log('fix random scolling');
37034 // this.scrollTo('top',0);
37037 content = content || this.content;
37039 this.setContent(content);
37041 if(config && config.url){
37042 this.setUrl(this.url, this.params, this.loadOnce);
37047 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37049 if (this.view && typeof(this.view.xtype) != 'undefined') {
37050 this.view.el = this.el.appendChild(document.createElement("div"));
37051 this.view = Roo.factory(this.view);
37052 this.view.render && this.view.render(false, '');
37056 this.fireEvent('render', this);
37059 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37063 setRegion : function(region){
37064 this.region = region;
37065 this.setActiveClass(region && !this.background);
37069 setActiveClass: function(state)
37072 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37073 this.el.setStyle('position','relative');
37075 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37076 this.el.setStyle('position', 'absolute');
37081 * Returns the toolbar for this Panel if one was configured.
37082 * @return {Roo.Toolbar}
37084 getToolbar : function(){
37085 return this.toolbar;
37088 setActiveState : function(active)
37090 this.active = active;
37091 this.setActiveClass(active);
37093 if(this.fireEvent("deactivate", this) === false){
37098 this.fireEvent("activate", this);
37102 * Updates this panel's element
37103 * @param {String} content The new content
37104 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37106 setContent : function(content, loadScripts){
37107 this.el.update(content, loadScripts);
37110 ignoreResize : function(w, h){
37111 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37114 this.lastSize = {width: w, height: h};
37119 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37120 * @return {Roo.UpdateManager} The UpdateManager
37122 getUpdateManager : function(){
37123 return this.el.getUpdateManager();
37126 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37127 * @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:
37130 url: "your-url.php",
37131 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37132 callback: yourFunction,
37133 scope: yourObject, //(optional scope)
37136 text: "Loading...",
37141 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37142 * 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.
37143 * @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}
37144 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37145 * @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.
37146 * @return {Roo.ContentPanel} this
37149 var um = this.el.getUpdateManager();
37150 um.update.apply(um, arguments);
37156 * 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.
37157 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37158 * @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)
37159 * @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)
37160 * @return {Roo.UpdateManager} The UpdateManager
37162 setUrl : function(url, params, loadOnce){
37163 if(this.refreshDelegate){
37164 this.removeListener("activate", this.refreshDelegate);
37166 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37167 this.on("activate", this.refreshDelegate);
37168 return this.el.getUpdateManager();
37171 _handleRefresh : function(url, params, loadOnce){
37172 if(!loadOnce || !this.loaded){
37173 var updater = this.el.getUpdateManager();
37174 updater.update(url, params, this._setLoaded.createDelegate(this));
37178 _setLoaded : function(){
37179 this.loaded = true;
37183 * Returns this panel's id
37186 getId : function(){
37191 * Returns this panel's element - used by regiosn to add.
37192 * @return {Roo.Element}
37194 getEl : function(){
37195 return this.wrapEl || this.el;
37200 adjustForComponents : function(width, height)
37202 //Roo.log('adjustForComponents ');
37203 if(this.resizeEl != this.el){
37204 width -= this.el.getFrameWidth('lr');
37205 height -= this.el.getFrameWidth('tb');
37208 var te = this.toolbar.getEl();
37209 te.setWidth(width);
37210 height -= te.getHeight();
37213 var te = this.footer.getEl();
37214 te.setWidth(width);
37215 height -= te.getHeight();
37219 if(this.adjustments){
37220 width += this.adjustments[0];
37221 height += this.adjustments[1];
37223 return {"width": width, "height": height};
37226 setSize : function(width, height){
37227 if(this.fitToFrame && !this.ignoreResize(width, height)){
37228 if(this.fitContainer && this.resizeEl != this.el){
37229 this.el.setSize(width, height);
37231 var size = this.adjustForComponents(width, height);
37232 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37233 this.fireEvent('resize', this, size.width, size.height);
37238 * Returns this panel's title
37241 getTitle : function(){
37243 if (typeof(this.title) != 'object') {
37248 for (var k in this.title) {
37249 if (!this.title.hasOwnProperty(k)) {
37253 if (k.indexOf('-') >= 0) {
37254 var s = k.split('-');
37255 for (var i = 0; i<s.length; i++) {
37256 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37259 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37266 * Set this panel's title
37267 * @param {String} title
37269 setTitle : function(title){
37270 this.title = title;
37272 this.region.updatePanelTitle(this, title);
37277 * Returns true is this panel was configured to be closable
37278 * @return {Boolean}
37280 isClosable : function(){
37281 return this.closable;
37284 beforeSlide : function(){
37286 this.resizeEl.clip();
37289 afterSlide : function(){
37291 this.resizeEl.unclip();
37295 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37296 * Will fail silently if the {@link #setUrl} method has not been called.
37297 * This does not activate the panel, just updates its content.
37299 refresh : function(){
37300 if(this.refreshDelegate){
37301 this.loaded = false;
37302 this.refreshDelegate();
37307 * Destroys this panel
37309 destroy : function(){
37310 this.el.removeAllListeners();
37311 var tempEl = document.createElement("span");
37312 tempEl.appendChild(this.el.dom);
37313 tempEl.innerHTML = "";
37319 * form - if the content panel contains a form - this is a reference to it.
37320 * @type {Roo.form.Form}
37324 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37325 * This contains a reference to it.
37331 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37341 * @param {Object} cfg Xtype definition of item to add.
37345 getChildContainer: function () {
37346 return this.getEl();
37351 var ret = new Roo.factory(cfg);
37356 if (cfg.xtype.match(/^Form$/)) {
37359 //if (this.footer) {
37360 // el = this.footer.container.insertSibling(false, 'before');
37362 el = this.el.createChild();
37365 this.form = new Roo.form.Form(cfg);
37368 if ( this.form.allItems.length) {
37369 this.form.render(el.dom);
37373 // should only have one of theses..
37374 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37375 // views.. should not be just added - used named prop 'view''
37377 cfg.el = this.el.appendChild(document.createElement("div"));
37380 var ret = new Roo.factory(cfg);
37382 ret.render && ret.render(false, ''); // render blank..
37392 * @class Roo.bootstrap.panel.Grid
37393 * @extends Roo.bootstrap.panel.Content
37395 * Create a new GridPanel.
37396 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37397 * @param {Object} config A the config object
37403 Roo.bootstrap.panel.Grid = function(config)
37407 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37408 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37410 config.el = this.wrapper;
37411 //this.el = this.wrapper;
37413 if (config.container) {
37414 // ctor'ed from a Border/panel.grid
37417 this.wrapper.setStyle("overflow", "hidden");
37418 this.wrapper.addClass('roo-grid-container');
37423 if(config.toolbar){
37424 var tool_el = this.wrapper.createChild();
37425 this.toolbar = Roo.factory(config.toolbar);
37427 if (config.toolbar.items) {
37428 ti = config.toolbar.items ;
37429 delete config.toolbar.items ;
37433 this.toolbar.render(tool_el);
37434 for(var i =0;i < ti.length;i++) {
37435 // Roo.log(['add child', items[i]]);
37436 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37438 this.toolbar.items = nitems;
37440 delete config.toolbar;
37443 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37444 config.grid.scrollBody = true;;
37445 config.grid.monitorWindowResize = false; // turn off autosizing
37446 config.grid.autoHeight = false;
37447 config.grid.autoWidth = false;
37449 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37451 if (config.background) {
37452 // render grid on panel activation (if panel background)
37453 this.on('activate', function(gp) {
37454 if (!gp.grid.rendered) {
37455 gp.grid.render(this.wrapper);
37456 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37461 this.grid.render(this.wrapper);
37462 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37465 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37466 // ??? needed ??? config.el = this.wrapper;
37471 // xtype created footer. - not sure if will work as we normally have to render first..
37472 if (this.footer && !this.footer.el && this.footer.xtype) {
37474 var ctr = this.grid.getView().getFooterPanel(true);
37475 this.footer.dataSource = this.grid.dataSource;
37476 this.footer = Roo.factory(this.footer, Roo);
37477 this.footer.render(ctr);
37487 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37488 getId : function(){
37489 return this.grid.id;
37493 * Returns the grid for this panel
37494 * @return {Roo.bootstrap.Table}
37496 getGrid : function(){
37500 setSize : function(width, height){
37501 if(!this.ignoreResize(width, height)){
37502 var grid = this.grid;
37503 var size = this.adjustForComponents(width, height);
37504 var gridel = grid.getGridEl();
37505 gridel.setSize(size.width, size.height);
37507 var thd = grid.getGridEl().select('thead',true).first();
37508 var tbd = grid.getGridEl().select('tbody', true).first();
37510 tbd.setSize(width, height - thd.getHeight());
37519 beforeSlide : function(){
37520 this.grid.getView().scroller.clip();
37523 afterSlide : function(){
37524 this.grid.getView().scroller.unclip();
37527 destroy : function(){
37528 this.grid.destroy();
37530 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37535 * @class Roo.bootstrap.panel.Nest
37536 * @extends Roo.bootstrap.panel.Content
37538 * Create a new Panel, that can contain a layout.Border.
37541 * @param {Roo.BorderLayout} layout The layout for this panel
37542 * @param {String/Object} config A string to set only the title or a config object
37544 Roo.bootstrap.panel.Nest = function(config)
37546 // construct with only one argument..
37547 /* FIXME - implement nicer consturctors
37548 if (layout.layout) {
37550 layout = config.layout;
37551 delete config.layout;
37553 if (layout.xtype && !layout.getEl) {
37554 // then layout needs constructing..
37555 layout = Roo.factory(layout, Roo);
37559 config.el = config.layout.getEl();
37561 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37563 config.layout.monitorWindowResize = false; // turn off autosizing
37564 this.layout = config.layout;
37565 this.layout.getEl().addClass("roo-layout-nested-layout");
37572 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37574 setSize : function(width, height){
37575 if(!this.ignoreResize(width, height)){
37576 var size = this.adjustForComponents(width, height);
37577 var el = this.layout.getEl();
37578 if (size.height < 1) {
37579 el.setWidth(size.width);
37581 el.setSize(size.width, size.height);
37583 var touch = el.dom.offsetWidth;
37584 this.layout.layout();
37585 // ie requires a double layout on the first pass
37586 if(Roo.isIE && !this.initialized){
37587 this.initialized = true;
37588 this.layout.layout();
37593 // activate all subpanels if not currently active..
37595 setActiveState : function(active){
37596 this.active = active;
37597 this.setActiveClass(active);
37600 this.fireEvent("deactivate", this);
37604 this.fireEvent("activate", this);
37605 // not sure if this should happen before or after..
37606 if (!this.layout) {
37607 return; // should not happen..
37610 for (var r in this.layout.regions) {
37611 reg = this.layout.getRegion(r);
37612 if (reg.getActivePanel()) {
37613 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37614 reg.setActivePanel(reg.getActivePanel());
37617 if (!reg.panels.length) {
37620 reg.showPanel(reg.getPanel(0));
37629 * Returns the nested BorderLayout for this panel
37630 * @return {Roo.BorderLayout}
37632 getLayout : function(){
37633 return this.layout;
37637 * Adds a xtype elements to the layout of the nested panel
37641 xtype : 'ContentPanel',
37648 xtype : 'NestedLayoutPanel',
37654 items : [ ... list of content panels or nested layout panels.. ]
37658 * @param {Object} cfg Xtype definition of item to add.
37660 addxtype : function(cfg) {
37661 return this.layout.addxtype(cfg);
37666 * Ext JS Library 1.1.1
37667 * Copyright(c) 2006-2007, Ext JS, LLC.
37669 * Originally Released Under LGPL - original licence link has changed is not relivant.
37672 * <script type="text/javascript">
37675 * @class Roo.TabPanel
37676 * @extends Roo.util.Observable
37677 * A lightweight tab container.
37681 // basic tabs 1, built from existing content
37682 var tabs = new Roo.TabPanel("tabs1");
37683 tabs.addTab("script", "View Script");
37684 tabs.addTab("markup", "View Markup");
37685 tabs.activate("script");
37687 // more advanced tabs, built from javascript
37688 var jtabs = new Roo.TabPanel("jtabs");
37689 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37691 // set up the UpdateManager
37692 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37693 var updater = tab2.getUpdateManager();
37694 updater.setDefaultUrl("ajax1.htm");
37695 tab2.on('activate', updater.refresh, updater, true);
37697 // Use setUrl for Ajax loading
37698 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37699 tab3.setUrl("ajax2.htm", null, true);
37702 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37705 jtabs.activate("jtabs-1");
37708 * Create a new TabPanel.
37709 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37710 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37712 Roo.bootstrap.panel.Tabs = function(config){
37714 * The container element for this TabPanel.
37715 * @type Roo.Element
37717 this.el = Roo.get(config.el);
37720 if(typeof config == "boolean"){
37721 this.tabPosition = config ? "bottom" : "top";
37723 Roo.apply(this, config);
37727 if(this.tabPosition == "bottom"){
37728 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37729 this.el.addClass("roo-tabs-bottom");
37731 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37732 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37733 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37735 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37737 if(this.tabPosition != "bottom"){
37738 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37739 * @type Roo.Element
37741 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37742 this.el.addClass("roo-tabs-top");
37746 this.bodyEl.setStyle("position", "relative");
37748 this.active = null;
37749 this.activateDelegate = this.activate.createDelegate(this);
37754 * Fires when the active tab changes
37755 * @param {Roo.TabPanel} this
37756 * @param {Roo.TabPanelItem} activePanel The new active tab
37760 * @event beforetabchange
37761 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37762 * @param {Roo.TabPanel} this
37763 * @param {Object} e Set cancel to true on this object to cancel the tab change
37764 * @param {Roo.TabPanelItem} tab The tab being changed to
37766 "beforetabchange" : true
37769 Roo.EventManager.onWindowResize(this.onResize, this);
37770 this.cpad = this.el.getPadding("lr");
37771 this.hiddenCount = 0;
37774 // toolbar on the tabbar support...
37775 if (this.toolbar) {
37776 alert("no toolbar support yet");
37777 this.toolbar = false;
37779 var tcfg = this.toolbar;
37780 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37781 this.toolbar = new Roo.Toolbar(tcfg);
37782 if (Roo.isSafari) {
37783 var tbl = tcfg.container.child('table', true);
37784 tbl.setAttribute('width', '100%');
37792 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37795 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37797 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37799 tabPosition : "top",
37801 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37803 currentTabWidth : 0,
37805 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37809 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37813 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37815 preferredTabWidth : 175,
37817 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37819 resizeTabs : false,
37821 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37823 monitorResize : true,
37825 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37830 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37831 * @param {String} id The id of the div to use <b>or create</b>
37832 * @param {String} text The text for the tab
37833 * @param {String} content (optional) Content to put in the TabPanelItem body
37834 * @param {Boolean} closable (optional) True to create a close icon on the tab
37835 * @return {Roo.TabPanelItem} The created TabPanelItem
37837 addTab : function(id, text, content, closable, tpl)
37839 var item = new Roo.bootstrap.panel.TabItem({
37843 closable : closable,
37846 this.addTabItem(item);
37848 item.setContent(content);
37854 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37855 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37856 * @return {Roo.TabPanelItem}
37858 getTab : function(id){
37859 return this.items[id];
37863 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37864 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37866 hideTab : function(id){
37867 var t = this.items[id];
37870 this.hiddenCount++;
37871 this.autoSizeTabs();
37876 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37877 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37879 unhideTab : function(id){
37880 var t = this.items[id];
37882 t.setHidden(false);
37883 this.hiddenCount--;
37884 this.autoSizeTabs();
37889 * Adds an existing {@link Roo.TabPanelItem}.
37890 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37892 addTabItem : function(item){
37893 this.items[item.id] = item;
37894 this.items.push(item);
37895 // if(this.resizeTabs){
37896 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37897 // this.autoSizeTabs();
37899 // item.autoSize();
37904 * Removes a {@link Roo.TabPanelItem}.
37905 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37907 removeTab : function(id){
37908 var items = this.items;
37909 var tab = items[id];
37910 if(!tab) { return; }
37911 var index = items.indexOf(tab);
37912 if(this.active == tab && items.length > 1){
37913 var newTab = this.getNextAvailable(index);
37918 this.stripEl.dom.removeChild(tab.pnode.dom);
37919 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37920 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37922 items.splice(index, 1);
37923 delete this.items[tab.id];
37924 tab.fireEvent("close", tab);
37925 tab.purgeListeners();
37926 this.autoSizeTabs();
37929 getNextAvailable : function(start){
37930 var items = this.items;
37932 // look for a next tab that will slide over to
37933 // replace the one being removed
37934 while(index < items.length){
37935 var item = items[++index];
37936 if(item && !item.isHidden()){
37940 // if one isn't found select the previous tab (on the left)
37943 var item = items[--index];
37944 if(item && !item.isHidden()){
37952 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37953 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37955 disableTab : function(id){
37956 var tab = this.items[id];
37957 if(tab && this.active != tab){
37963 * Enables a {@link Roo.TabPanelItem} that is disabled.
37964 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37966 enableTab : function(id){
37967 var tab = this.items[id];
37972 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37973 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37974 * @return {Roo.TabPanelItem} The TabPanelItem.
37976 activate : function(id){
37977 var tab = this.items[id];
37981 if(tab == this.active || tab.disabled){
37985 this.fireEvent("beforetabchange", this, e, tab);
37986 if(e.cancel !== true && !tab.disabled){
37988 this.active.hide();
37990 this.active = this.items[id];
37991 this.active.show();
37992 this.fireEvent("tabchange", this, this.active);
37998 * Gets the active {@link Roo.TabPanelItem}.
37999 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38001 getActiveTab : function(){
38002 return this.active;
38006 * Updates the tab body element to fit the height of the container element
38007 * for overflow scrolling
38008 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38010 syncHeight : function(targetHeight){
38011 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38012 var bm = this.bodyEl.getMargins();
38013 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38014 this.bodyEl.setHeight(newHeight);
38018 onResize : function(){
38019 if(this.monitorResize){
38020 this.autoSizeTabs();
38025 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38027 beginUpdate : function(){
38028 this.updating = true;
38032 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38034 endUpdate : function(){
38035 this.updating = false;
38036 this.autoSizeTabs();
38040 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38042 autoSizeTabs : function(){
38043 var count = this.items.length;
38044 var vcount = count - this.hiddenCount;
38045 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38048 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38049 var availWidth = Math.floor(w / vcount);
38050 var b = this.stripBody;
38051 if(b.getWidth() > w){
38052 var tabs = this.items;
38053 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38054 if(availWidth < this.minTabWidth){
38055 /*if(!this.sleft){ // incomplete scrolling code
38056 this.createScrollButtons();
38059 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38062 if(this.currentTabWidth < this.preferredTabWidth){
38063 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38069 * Returns the number of tabs in this TabPanel.
38072 getCount : function(){
38073 return this.items.length;
38077 * Resizes all the tabs to the passed width
38078 * @param {Number} The new width
38080 setTabWidth : function(width){
38081 this.currentTabWidth = width;
38082 for(var i = 0, len = this.items.length; i < len; i++) {
38083 if(!this.items[i].isHidden()) {
38084 this.items[i].setWidth(width);
38090 * Destroys this TabPanel
38091 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38093 destroy : function(removeEl){
38094 Roo.EventManager.removeResizeListener(this.onResize, this);
38095 for(var i = 0, len = this.items.length; i < len; i++){
38096 this.items[i].purgeListeners();
38098 if(removeEl === true){
38099 this.el.update("");
38104 createStrip : function(container)
38106 var strip = document.createElement("nav");
38107 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38108 container.appendChild(strip);
38112 createStripList : function(strip)
38114 // div wrapper for retard IE
38115 // returns the "tr" element.
38116 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38117 //'<div class="x-tabs-strip-wrap">'+
38118 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38119 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38120 return strip.firstChild; //.firstChild.firstChild.firstChild;
38122 createBody : function(container)
38124 var body = document.createElement("div");
38125 Roo.id(body, "tab-body");
38126 //Roo.fly(body).addClass("x-tabs-body");
38127 Roo.fly(body).addClass("tab-content");
38128 container.appendChild(body);
38131 createItemBody :function(bodyEl, id){
38132 var body = Roo.getDom(id);
38134 body = document.createElement("div");
38137 //Roo.fly(body).addClass("x-tabs-item-body");
38138 Roo.fly(body).addClass("tab-pane");
38139 bodyEl.insertBefore(body, bodyEl.firstChild);
38143 createStripElements : function(stripEl, text, closable, tpl)
38145 var td = document.createElement("li"); // was td..
38148 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38151 stripEl.appendChild(td);
38153 td.className = "x-tabs-closable";
38154 if(!this.closeTpl){
38155 this.closeTpl = new Roo.Template(
38156 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38157 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38158 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38161 var el = this.closeTpl.overwrite(td, {"text": text});
38162 var close = el.getElementsByTagName("div")[0];
38163 var inner = el.getElementsByTagName("em")[0];
38164 return {"el": el, "close": close, "inner": inner};
38167 // not sure what this is..
38168 // if(!this.tabTpl){
38169 //this.tabTpl = new Roo.Template(
38170 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38171 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38173 // this.tabTpl = new Roo.Template(
38174 // '<a href="#">' +
38175 // '<span unselectable="on"' +
38176 // (this.disableTooltips ? '' : ' title="{text}"') +
38177 // ' >{text}</span></a>'
38183 var template = tpl || this.tabTpl || false;
38187 template = new Roo.Template(
38189 '<span unselectable="on"' +
38190 (this.disableTooltips ? '' : ' title="{text}"') +
38191 ' >{text}</span></a>'
38195 switch (typeof(template)) {
38199 template = new Roo.Template(template);
38205 var el = template.overwrite(td, {"text": text});
38207 var inner = el.getElementsByTagName("span")[0];
38209 return {"el": el, "inner": inner};
38217 * @class Roo.TabPanelItem
38218 * @extends Roo.util.Observable
38219 * Represents an individual item (tab plus body) in a TabPanel.
38220 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38221 * @param {String} id The id of this TabPanelItem
38222 * @param {String} text The text for the tab of this TabPanelItem
38223 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38225 Roo.bootstrap.panel.TabItem = function(config){
38227 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38228 * @type Roo.TabPanel
38230 this.tabPanel = config.panel;
38232 * The id for this TabPanelItem
38235 this.id = config.id;
38237 this.disabled = false;
38239 this.text = config.text;
38241 this.loaded = false;
38242 this.closable = config.closable;
38245 * The body element for this TabPanelItem.
38246 * @type Roo.Element
38248 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38249 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38250 this.bodyEl.setStyle("display", "block");
38251 this.bodyEl.setStyle("zoom", "1");
38252 //this.hideAction();
38254 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38256 this.el = Roo.get(els.el);
38257 this.inner = Roo.get(els.inner, true);
38258 this.textEl = Roo.get(this.el.dom.firstChild, true);
38259 this.pnode = Roo.get(els.el.parentNode, true);
38260 // this.el.on("mousedown", this.onTabMouseDown, this);
38261 this.el.on("click", this.onTabClick, this);
38263 if(config.closable){
38264 var c = Roo.get(els.close, true);
38265 c.dom.title = this.closeText;
38266 c.addClassOnOver("close-over");
38267 c.on("click", this.closeClick, this);
38273 * Fires when this tab becomes the active tab.
38274 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38275 * @param {Roo.TabPanelItem} this
38279 * @event beforeclose
38280 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38281 * @param {Roo.TabPanelItem} this
38282 * @param {Object} e Set cancel to true on this object to cancel the close.
38284 "beforeclose": true,
38287 * Fires when this tab is closed.
38288 * @param {Roo.TabPanelItem} this
38292 * @event deactivate
38293 * Fires when this tab is no longer the active tab.
38294 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38295 * @param {Roo.TabPanelItem} this
38297 "deactivate" : true
38299 this.hidden = false;
38301 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38304 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38306 purgeListeners : function(){
38307 Roo.util.Observable.prototype.purgeListeners.call(this);
38308 this.el.removeAllListeners();
38311 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38314 this.pnode.addClass("active");
38317 this.tabPanel.stripWrap.repaint();
38319 this.fireEvent("activate", this.tabPanel, this);
38323 * Returns true if this tab is the active tab.
38324 * @return {Boolean}
38326 isActive : function(){
38327 return this.tabPanel.getActiveTab() == this;
38331 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38334 this.pnode.removeClass("active");
38336 this.fireEvent("deactivate", this.tabPanel, this);
38339 hideAction : function(){
38340 this.bodyEl.hide();
38341 this.bodyEl.setStyle("position", "absolute");
38342 this.bodyEl.setLeft("-20000px");
38343 this.bodyEl.setTop("-20000px");
38346 showAction : function(){
38347 this.bodyEl.setStyle("position", "relative");
38348 this.bodyEl.setTop("");
38349 this.bodyEl.setLeft("");
38350 this.bodyEl.show();
38354 * Set the tooltip for the tab.
38355 * @param {String} tooltip The tab's tooltip
38357 setTooltip : function(text){
38358 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38359 this.textEl.dom.qtip = text;
38360 this.textEl.dom.removeAttribute('title');
38362 this.textEl.dom.title = text;
38366 onTabClick : function(e){
38367 e.preventDefault();
38368 this.tabPanel.activate(this.id);
38371 onTabMouseDown : function(e){
38372 e.preventDefault();
38373 this.tabPanel.activate(this.id);
38376 getWidth : function(){
38377 return this.inner.getWidth();
38380 setWidth : function(width){
38381 var iwidth = width - this.pnode.getPadding("lr");
38382 this.inner.setWidth(iwidth);
38383 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38384 this.pnode.setWidth(width);
38388 * Show or hide the tab
38389 * @param {Boolean} hidden True to hide or false to show.
38391 setHidden : function(hidden){
38392 this.hidden = hidden;
38393 this.pnode.setStyle("display", hidden ? "none" : "");
38397 * Returns true if this tab is "hidden"
38398 * @return {Boolean}
38400 isHidden : function(){
38401 return this.hidden;
38405 * Returns the text for this tab
38408 getText : function(){
38412 autoSize : function(){
38413 //this.el.beginMeasure();
38414 this.textEl.setWidth(1);
38416 * #2804 [new] Tabs in Roojs
38417 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38419 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38420 //this.el.endMeasure();
38424 * Sets the text for the tab (Note: this also sets the tooltip text)
38425 * @param {String} text The tab's text and tooltip
38427 setText : function(text){
38429 this.textEl.update(text);
38430 this.setTooltip(text);
38431 //if(!this.tabPanel.resizeTabs){
38432 // this.autoSize();
38436 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38438 activate : function(){
38439 this.tabPanel.activate(this.id);
38443 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38445 disable : function(){
38446 if(this.tabPanel.active != this){
38447 this.disabled = true;
38448 this.pnode.addClass("disabled");
38453 * Enables this TabPanelItem if it was previously disabled.
38455 enable : function(){
38456 this.disabled = false;
38457 this.pnode.removeClass("disabled");
38461 * Sets the content for this TabPanelItem.
38462 * @param {String} content The content
38463 * @param {Boolean} loadScripts true to look for and load scripts
38465 setContent : function(content, loadScripts){
38466 this.bodyEl.update(content, loadScripts);
38470 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38471 * @return {Roo.UpdateManager} The UpdateManager
38473 getUpdateManager : function(){
38474 return this.bodyEl.getUpdateManager();
38478 * Set a URL to be used to load the content for this TabPanelItem.
38479 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38480 * @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)
38481 * @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)
38482 * @return {Roo.UpdateManager} The UpdateManager
38484 setUrl : function(url, params, loadOnce){
38485 if(this.refreshDelegate){
38486 this.un('activate', this.refreshDelegate);
38488 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38489 this.on("activate", this.refreshDelegate);
38490 return this.bodyEl.getUpdateManager();
38494 _handleRefresh : function(url, params, loadOnce){
38495 if(!loadOnce || !this.loaded){
38496 var updater = this.bodyEl.getUpdateManager();
38497 updater.update(url, params, this._setLoaded.createDelegate(this));
38502 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38503 * Will fail silently if the setUrl method has not been called.
38504 * This does not activate the panel, just updates its content.
38506 refresh : function(){
38507 if(this.refreshDelegate){
38508 this.loaded = false;
38509 this.refreshDelegate();
38514 _setLoaded : function(){
38515 this.loaded = true;
38519 closeClick : function(e){
38522 this.fireEvent("beforeclose", this, o);
38523 if(o.cancel !== true){
38524 this.tabPanel.removeTab(this.id);
38528 * The text displayed in the tooltip for the close icon.
38531 closeText : "Close this tab"
38534 * This script refer to:
38535 * Title: International Telephone Input
38536 * Author: Jack O'Connor
38537 * Code version: v12.1.12
38538 * Availability: https://github.com/jackocnr/intl-tel-input.git
38541 Roo.bootstrap.PhoneInputData = function() {
38544 "Afghanistan (افغانستان)",
38549 "Albania (Shqipëri)",
38554 "Algeria (الجزائر)",
38579 "Antigua and Barbuda",
38589 "Armenia (Հայաստան)",
38605 "Austria (Österreich)",
38610 "Azerbaijan (Azərbaycan)",
38620 "Bahrain (البحرين)",
38625 "Bangladesh (বাংলাদেশ)",
38635 "Belarus (Беларусь)",
38640 "Belgium (België)",
38670 "Bosnia and Herzegovina (Босна и Херцеговина)",
38685 "British Indian Ocean Territory",
38690 "British Virgin Islands",
38700 "Bulgaria (България)",
38710 "Burundi (Uburundi)",
38715 "Cambodia (កម្ពុជា)",
38720 "Cameroon (Cameroun)",
38729 ["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"]
38732 "Cape Verde (Kabu Verdi)",
38737 "Caribbean Netherlands",
38748 "Central African Republic (République centrafricaine)",
38768 "Christmas Island",
38774 "Cocos (Keeling) Islands",
38785 "Comoros (جزر القمر)",
38790 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38795 "Congo (Republic) (Congo-Brazzaville)",
38815 "Croatia (Hrvatska)",
38836 "Czech Republic (Česká republika)",
38841 "Denmark (Danmark)",
38856 "Dominican Republic (República Dominicana)",
38860 ["809", "829", "849"]
38878 "Equatorial Guinea (Guinea Ecuatorial)",
38898 "Falkland Islands (Islas Malvinas)",
38903 "Faroe Islands (Føroyar)",
38924 "French Guiana (Guyane française)",
38929 "French Polynesia (Polynésie française)",
38944 "Georgia (საქართველო)",
38949 "Germany (Deutschland)",
38969 "Greenland (Kalaallit Nunaat)",
39006 "Guinea-Bissau (Guiné Bissau)",
39031 "Hungary (Magyarország)",
39036 "Iceland (Ísland)",
39056 "Iraq (العراق)",
39072 "Israel (ישראל)",
39099 "Jordan (الأردن)",
39104 "Kazakhstan (Казахстан)",
39125 "Kuwait (الكويت)",
39130 "Kyrgyzstan (Кыргызстан)",
39140 "Latvia (Latvija)",
39145 "Lebanon (لبنان)",
39160 "Libya (ليبيا)",
39170 "Lithuania (Lietuva)",
39185 "Macedonia (FYROM) (Македонија)",
39190 "Madagascar (Madagasikara)",
39220 "Marshall Islands",
39230 "Mauritania (موريتانيا)",
39235 "Mauritius (Moris)",
39256 "Moldova (Republica Moldova)",
39266 "Mongolia (Монгол)",
39271 "Montenegro (Crna Gora)",
39281 "Morocco (المغرب)",
39287 "Mozambique (Moçambique)",
39292 "Myanmar (Burma) (မြန်မာ)",
39297 "Namibia (Namibië)",
39312 "Netherlands (Nederland)",
39317 "New Caledonia (Nouvelle-Calédonie)",
39352 "North Korea (조선 민주주의 인민 공화국)",
39357 "Northern Mariana Islands",
39373 "Pakistan (پاکستان)",
39383 "Palestine (فلسطين)",
39393 "Papua New Guinea",
39435 "Réunion (La Réunion)",
39441 "Romania (România)",
39457 "Saint Barthélemy",
39468 "Saint Kitts and Nevis",
39478 "Saint Martin (Saint-Martin (partie française))",
39484 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39489 "Saint Vincent and the Grenadines",
39504 "São Tomé and Príncipe (São Tomé e Príncipe)",
39509 "Saudi Arabia (المملكة العربية السعودية)",
39514 "Senegal (Sénégal)",
39544 "Slovakia (Slovensko)",
39549 "Slovenia (Slovenija)",
39559 "Somalia (Soomaaliya)",
39569 "South Korea (대한민국)",
39574 "South Sudan (جنوب السودان)",
39584 "Sri Lanka (ශ්රී ලංකාව)",
39589 "Sudan (السودان)",
39599 "Svalbard and Jan Mayen",
39610 "Sweden (Sverige)",
39615 "Switzerland (Schweiz)",
39620 "Syria (سوريا)",
39665 "Trinidad and Tobago",
39670 "Tunisia (تونس)",
39675 "Turkey (Türkiye)",
39685 "Turks and Caicos Islands",
39695 "U.S. Virgin Islands",
39705 "Ukraine (Україна)",
39710 "United Arab Emirates (الإمارات العربية المتحدة)",
39732 "Uzbekistan (Oʻzbekiston)",
39742 "Vatican City (Città del Vaticano)",
39753 "Vietnam (Việt Nam)",
39758 "Wallis and Futuna (Wallis-et-Futuna)",
39763 "Western Sahara (الصحراء الغربية)",
39769 "Yemen (اليمن)",
39793 * This script refer to:
39794 * Title: International Telephone Input
39795 * Author: Jack O'Connor
39796 * Code version: v12.1.12
39797 * Availability: https://github.com/jackocnr/intl-tel-input.git
39801 * @class Roo.bootstrap.PhoneInput
39802 * @extends Roo.bootstrap.TriggerField
39803 * An input with International dial-code selection
39805 * @cfg {String} defaultDialCode default '+852'
39806 * @cfg {Array} preferedCountries default []
39809 * Create a new PhoneInput.
39810 * @param {Object} config Configuration options
39813 Roo.bootstrap.PhoneInput = function(config) {
39814 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39817 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39819 listWidth: undefined,
39821 selectedClass: 'active',
39823 invalidClass : "has-warning",
39825 validClass: 'has-success',
39827 allowed: '0123456789',
39830 * @cfg {String} defaultDialCode The default dial code when initializing the input
39832 defaultDialCode: '+852',
39835 * @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
39837 preferedCountries: false,
39839 getAutoCreate : function()
39841 var data = Roo.bootstrap.PhoneInputData();
39842 var align = this.labelAlign || this.parentLabelAlign();
39845 this.allCountries = [];
39846 this.dialCodeMapping = [];
39848 for (var i = 0; i < data.length; i++) {
39850 this.allCountries[i] = {
39854 priority: c[3] || 0,
39855 areaCodes: c[4] || null
39857 this.dialCodeMapping[c[2]] = {
39860 priority: c[3] || 0,
39861 areaCodes: c[4] || null
39873 cls : 'form-control tel-input',
39874 autocomplete: 'new-password'
39877 var hiddenInput = {
39880 cls: 'hidden-tel-input'
39884 hiddenInput.name = this.name;
39887 if (this.disabled) {
39888 input.disabled = true;
39891 var flag_container = {
39908 cls: this.hasFeedback ? 'has-feedback' : '',
39914 cls: 'dial-code-holder',
39921 cls: 'roo-select2-container input-group',
39928 if (this.fieldLabel.length) {
39931 tooltip: 'This field is required'
39937 cls: 'control-label',
39943 html: this.fieldLabel
39946 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39952 if(this.indicatorpos == 'right') {
39953 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39960 if(align == 'left') {
39968 if(this.labelWidth > 12){
39969 label.style = "width: " + this.labelWidth + 'px';
39971 if(this.labelWidth < 13 && this.labelmd == 0){
39972 this.labelmd = this.labelWidth;
39974 if(this.labellg > 0){
39975 label.cls += ' col-lg-' + this.labellg;
39976 input.cls += ' col-lg-' + (12 - this.labellg);
39978 if(this.labelmd > 0){
39979 label.cls += ' col-md-' + this.labelmd;
39980 container.cls += ' col-md-' + (12 - this.labelmd);
39982 if(this.labelsm > 0){
39983 label.cls += ' col-sm-' + this.labelsm;
39984 container.cls += ' col-sm-' + (12 - this.labelsm);
39986 if(this.labelxs > 0){
39987 label.cls += ' col-xs-' + this.labelxs;
39988 container.cls += ' col-xs-' + (12 - this.labelxs);
39998 var settings = this;
40000 ['xs','sm','md','lg'].map(function(size){
40001 if (settings[size]) {
40002 cfg.cls += ' col-' + size + '-' + settings[size];
40006 this.store = new Roo.data.Store({
40007 proxy : new Roo.data.MemoryProxy({}),
40008 reader : new Roo.data.JsonReader({
40019 'name' : 'dialCode',
40023 'name' : 'priority',
40027 'name' : 'areaCodes',
40034 if(!this.preferedCountries) {
40035 this.preferedCountries = [
40042 var p = this.preferedCountries.reverse();
40045 for (var i = 0; i < p.length; i++) {
40046 for (var j = 0; j < this.allCountries.length; j++) {
40047 if(this.allCountries[j].iso2 == p[i]) {
40048 var t = this.allCountries[j];
40049 this.allCountries.splice(j,1);
40050 this.allCountries.unshift(t);
40056 this.store.proxy.data = {
40058 data: this.allCountries
40064 initEvents : function()
40067 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40069 this.indicator = this.indicatorEl();
40070 this.flag = this.flagEl();
40071 this.dialCodeHolder = this.dialCodeHolderEl();
40073 this.trigger = this.el.select('div.flag-box',true).first();
40074 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40079 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40080 _this.list.setWidth(lw);
40083 this.list.on('mouseover', this.onViewOver, this);
40084 this.list.on('mousemove', this.onViewMove, this);
40085 this.inputEl().on("keyup", this.onKeyUp, this);
40087 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40089 this.view = new Roo.View(this.list, this.tpl, {
40090 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40093 this.view.on('click', this.onViewClick, this);
40094 this.setValue(this.defaultDialCode);
40097 onTriggerClick : function(e)
40099 Roo.log('trigger click');
40104 if(this.isExpanded()){
40106 this.hasFocus = false;
40108 this.store.load({});
40109 this.hasFocus = true;
40114 isExpanded : function()
40116 return this.list.isVisible();
40119 collapse : function()
40121 if(!this.isExpanded()){
40125 Roo.get(document).un('mousedown', this.collapseIf, this);
40126 Roo.get(document).un('mousewheel', this.collapseIf, this);
40127 this.fireEvent('collapse', this);
40131 expand : function()
40135 if(this.isExpanded() || !this.hasFocus){
40139 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40140 this.list.setWidth(lw);
40143 this.restrictHeight();
40145 Roo.get(document).on('mousedown', this.collapseIf, this);
40146 Roo.get(document).on('mousewheel', this.collapseIf, this);
40148 this.fireEvent('expand', this);
40151 restrictHeight : function()
40153 this.list.alignTo(this.inputEl(), this.listAlign);
40154 this.list.alignTo(this.inputEl(), this.listAlign);
40157 onViewOver : function(e, t)
40159 if(this.inKeyMode){
40162 var item = this.view.findItemFromChild(t);
40165 var index = this.view.indexOf(item);
40166 this.select(index, false);
40171 onViewClick : function(view, doFocus, el, e)
40173 var index = this.view.getSelectedIndexes()[0];
40175 var r = this.store.getAt(index);
40178 this.onSelect(r, index);
40180 if(doFocus !== false && !this.blockFocus){
40181 this.inputEl().focus();
40185 onViewMove : function(e, t)
40187 this.inKeyMode = false;
40190 select : function(index, scrollIntoView)
40192 this.selectedIndex = index;
40193 this.view.select(index);
40194 if(scrollIntoView !== false){
40195 var el = this.view.getNode(index);
40197 this.list.scrollChildIntoView(el, false);
40202 createList : function()
40204 this.list = Roo.get(document.body).createChild({
40206 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40207 style: 'display:none'
40210 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40213 collapseIf : function(e)
40215 var in_combo = e.within(this.el);
40216 var in_list = e.within(this.list);
40217 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40219 if (in_combo || in_list || is_list) {
40225 onSelect : function(record, index)
40227 if(this.fireEvent('beforeselect', this, record, index) !== false){
40229 this.setFlagClass(record.data.iso2);
40230 this.setDialCode(record.data.dialCode);
40231 this.hasFocus = false;
40233 this.fireEvent('select', this, record, index);
40237 flagEl : function()
40239 var flag = this.el.select('div.flag',true).first();
40246 dialCodeHolderEl : function()
40248 var d = this.el.select('input.dial-code-holder',true).first();
40255 setDialCode : function(v)
40257 this.dialCodeHolder.dom.value = '+'+v;
40260 setFlagClass : function(n)
40262 this.flag.dom.className = 'flag '+n;
40265 getValue : function()
40267 var v = this.inputEl().getValue();
40268 if(this.dialCodeHolder) {
40269 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40274 setValue : function(v)
40276 var d = this.getDialCode(v);
40278 //invalid dial code
40279 if(v.length == 0 || !d || d.length == 0) {
40281 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40282 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40288 this.setFlagClass(this.dialCodeMapping[d].iso2);
40289 this.setDialCode(d);
40290 this.inputEl().dom.value = v.replace('+'+d,'');
40291 this.hiddenEl().dom.value = this.getValue();
40296 getDialCode : function(v)
40300 if (v.length == 0) {
40301 return this.dialCodeHolder.dom.value;
40305 if (v.charAt(0) != "+") {
40308 var numericChars = "";
40309 for (var i = 1; i < v.length; i++) {
40310 var c = v.charAt(i);
40313 if (this.dialCodeMapping[numericChars]) {
40314 dialCode = v.substr(1, i);
40316 if (numericChars.length == 4) {
40326 this.setValue(this.defaultDialCode);
40330 hiddenEl : function()
40332 return this.el.select('input.hidden-tel-input',true).first();
40335 onKeyUp : function(e){
40337 var k = e.getKey();
40338 var c = e.getCharCode();
40341 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40342 this.allowed.indexOf(String.fromCharCode(c)) === -1
40347 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40350 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40354 this.setValue(this.getValue());
40359 * @class Roo.bootstrap.MoneyField
40360 * @extends Roo.bootstrap.ComboBox
40361 * Bootstrap MoneyField class
40364 * Create a new MoneyField.
40365 * @param {Object} config Configuration options
40368 Roo.bootstrap.MoneyField = function(config) {
40370 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40374 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40377 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40379 allowDecimals : true,
40381 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40383 decimalSeparator : ".",
40385 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40387 decimalPrecision : 0,
40389 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40391 allowNegative : true,
40393 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40397 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40399 minValue : Number.NEGATIVE_INFINITY,
40401 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40403 maxValue : Number.MAX_VALUE,
40405 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40407 minText : "The minimum value for this field is {0}",
40409 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40411 maxText : "The maximum value for this field is {0}",
40413 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40414 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40416 nanText : "{0} is not a valid number",
40418 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40422 * @cfg {String} defaults currency of the MoneyField
40423 * value should be in lkey
40425 defaultCurrency : false,
40427 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40429 thousandsDelimiter : false,
40439 getAutoCreate : function()
40441 var align = this.labelAlign || this.parentLabelAlign();
40453 cls : 'form-control roo-money-amount-input',
40454 autocomplete: 'new-password'
40457 var hiddenInput = {
40461 cls: 'hidden-number-input'
40465 hiddenInput.name = this.name;
40468 if (this.disabled) {
40469 input.disabled = true;
40472 var clg = 12 - this.inputlg;
40473 var cmd = 12 - this.inputmd;
40474 var csm = 12 - this.inputsm;
40475 var cxs = 12 - this.inputxs;
40479 cls : 'row roo-money-field',
40483 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40487 cls: 'roo-select2-container input-group',
40491 cls : 'form-control roo-money-currency-input',
40492 autocomplete: 'new-password',
40494 name : this.currencyName
40498 cls : 'input-group-addon',
40512 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40516 cls: this.hasFeedback ? 'has-feedback' : '',
40527 if (this.fieldLabel.length) {
40530 tooltip: 'This field is required'
40536 cls: 'control-label',
40542 html: this.fieldLabel
40545 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40551 if(this.indicatorpos == 'right') {
40552 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40559 if(align == 'left') {
40567 if(this.labelWidth > 12){
40568 label.style = "width: " + this.labelWidth + 'px';
40570 if(this.labelWidth < 13 && this.labelmd == 0){
40571 this.labelmd = this.labelWidth;
40573 if(this.labellg > 0){
40574 label.cls += ' col-lg-' + this.labellg;
40575 input.cls += ' col-lg-' + (12 - this.labellg);
40577 if(this.labelmd > 0){
40578 label.cls += ' col-md-' + this.labelmd;
40579 container.cls += ' col-md-' + (12 - this.labelmd);
40581 if(this.labelsm > 0){
40582 label.cls += ' col-sm-' + this.labelsm;
40583 container.cls += ' col-sm-' + (12 - this.labelsm);
40585 if(this.labelxs > 0){
40586 label.cls += ' col-xs-' + this.labelxs;
40587 container.cls += ' col-xs-' + (12 - this.labelxs);
40598 var settings = this;
40600 ['xs','sm','md','lg'].map(function(size){
40601 if (settings[size]) {
40602 cfg.cls += ' col-' + size + '-' + settings[size];
40609 initEvents : function()
40611 this.indicator = this.indicatorEl();
40613 this.initCurrencyEvent();
40615 this.initNumberEvent();
40618 initCurrencyEvent : function()
40621 throw "can not find store for combo";
40624 this.store = Roo.factory(this.store, Roo.data);
40625 this.store.parent = this;
40629 this.triggerEl = this.el.select('.input-group-addon', true).first();
40631 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40636 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40637 _this.list.setWidth(lw);
40640 this.list.on('mouseover', this.onViewOver, this);
40641 this.list.on('mousemove', this.onViewMove, this);
40642 this.list.on('scroll', this.onViewScroll, this);
40645 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40648 this.view = new Roo.View(this.list, this.tpl, {
40649 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40652 this.view.on('click', this.onViewClick, this);
40654 this.store.on('beforeload', this.onBeforeLoad, this);
40655 this.store.on('load', this.onLoad, this);
40656 this.store.on('loadexception', this.onLoadException, this);
40658 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40659 "up" : function(e){
40660 this.inKeyMode = true;
40664 "down" : function(e){
40665 if(!this.isExpanded()){
40666 this.onTriggerClick();
40668 this.inKeyMode = true;
40673 "enter" : function(e){
40676 if(this.fireEvent("specialkey", this, e)){
40677 this.onViewClick(false);
40683 "esc" : function(e){
40687 "tab" : function(e){
40690 if(this.fireEvent("specialkey", this, e)){
40691 this.onViewClick(false);
40699 doRelay : function(foo, bar, hname){
40700 if(hname == 'down' || this.scope.isExpanded()){
40701 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40709 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40713 initNumberEvent : function(e)
40715 this.inputEl().on("keydown" , this.fireKey, this);
40716 this.inputEl().on("focus", this.onFocus, this);
40717 this.inputEl().on("blur", this.onBlur, this);
40719 this.inputEl().relayEvent('keyup', this);
40721 if(this.indicator){
40722 this.indicator.addClass('invisible');
40725 this.originalValue = this.getValue();
40727 if(this.validationEvent == 'keyup'){
40728 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40729 this.inputEl().on('keyup', this.filterValidation, this);
40731 else if(this.validationEvent !== false){
40732 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40735 if(this.selectOnFocus){
40736 this.on("focus", this.preFocus, this);
40739 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40740 this.inputEl().on("keypress", this.filterKeys, this);
40742 this.inputEl().relayEvent('keypress', this);
40745 var allowed = "0123456789";
40747 if(this.allowDecimals){
40748 allowed += this.decimalSeparator;
40751 if(this.allowNegative){
40755 if(this.thousandsDelimiter) {
40759 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40761 var keyPress = function(e){
40763 var k = e.getKey();
40765 var c = e.getCharCode();
40768 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40769 allowed.indexOf(String.fromCharCode(c)) === -1
40775 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40779 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40784 this.inputEl().on("keypress", keyPress, this);
40788 onTriggerClick : function(e)
40795 this.loadNext = false;
40797 if(this.isExpanded()){
40802 this.hasFocus = true;
40804 if(this.triggerAction == 'all') {
40805 this.doQuery(this.allQuery, true);
40809 this.doQuery(this.getRawValue());
40812 getCurrency : function()
40814 var v = this.currencyEl().getValue();
40819 restrictHeight : function()
40821 this.list.alignTo(this.currencyEl(), this.listAlign);
40822 this.list.alignTo(this.currencyEl(), this.listAlign);
40825 onViewClick : function(view, doFocus, el, e)
40827 var index = this.view.getSelectedIndexes()[0];
40829 var r = this.store.getAt(index);
40832 this.onSelect(r, index);
40836 onSelect : function(record, index){
40838 if(this.fireEvent('beforeselect', this, record, index) !== false){
40840 this.setFromCurrencyData(index > -1 ? record.data : false);
40844 this.fireEvent('select', this, record, index);
40848 setFromCurrencyData : function(o)
40852 this.lastCurrency = o;
40854 if (this.currencyField) {
40855 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40857 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40860 this.lastSelectionText = currency;
40862 //setting default currency
40863 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40864 this.setCurrency(this.defaultCurrency);
40868 this.setCurrency(currency);
40871 setFromData : function(o)
40875 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40877 this.setFromCurrencyData(c);
40882 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40884 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40887 this.setValue(value);
40891 setCurrency : function(v)
40893 this.currencyValue = v;
40896 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40901 setValue : function(v)
40903 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40909 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40911 this.inputEl().dom.value = (v == '') ? '' :
40912 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40914 if(!this.allowZero && v === '0') {
40915 this.hiddenEl().dom.value = '';
40916 this.inputEl().dom.value = '';
40923 getRawValue : function()
40925 var v = this.inputEl().getValue();
40930 getValue : function()
40932 return this.fixPrecision(this.parseValue(this.getRawValue()));
40935 parseValue : function(value)
40937 if(this.thousandsDelimiter) {
40939 r = new RegExp(",", "g");
40940 value = value.replace(r, "");
40943 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40944 return isNaN(value) ? '' : value;
40948 fixPrecision : function(value)
40950 if(this.thousandsDelimiter) {
40952 r = new RegExp(",", "g");
40953 value = value.replace(r, "");
40956 var nan = isNaN(value);
40958 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40959 return nan ? '' : value;
40961 return parseFloat(value).toFixed(this.decimalPrecision);
40964 decimalPrecisionFcn : function(v)
40966 return Math.floor(v);
40969 validateValue : function(value)
40971 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40975 var num = this.parseValue(value);
40978 this.markInvalid(String.format(this.nanText, value));
40982 if(num < this.minValue){
40983 this.markInvalid(String.format(this.minText, this.minValue));
40987 if(num > this.maxValue){
40988 this.markInvalid(String.format(this.maxText, this.maxValue));
40995 validate : function()
40997 if(this.disabled || this.allowBlank){
41002 var currency = this.getCurrency();
41004 if(this.validateValue(this.getRawValue()) && currency.length){
41009 this.markInvalid();
41013 getName: function()
41018 beforeBlur : function()
41024 var v = this.parseValue(this.getRawValue());
41031 onBlur : function()
41035 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41036 //this.el.removeClass(this.focusClass);
41039 this.hasFocus = false;
41041 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41045 var v = this.getValue();
41047 if(String(v) !== String(this.startValue)){
41048 this.fireEvent('change', this, v, this.startValue);
41051 this.fireEvent("blur", this);
41054 inputEl : function()
41056 return this.el.select('.roo-money-amount-input', true).first();
41059 currencyEl : function()
41061 return this.el.select('.roo-money-currency-input', true).first();
41064 hiddenEl : function()
41066 return this.el.select('input.hidden-number-input',true).first();