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);
8324 showFields : function(items)
8326 Roo.each(items, function(i){
8328 var f = this.findField(i);
8341 Roo.apply(Roo.bootstrap.Form, {
8368 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8369 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8370 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8371 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8374 this.maskEl.top.enableDisplayMode("block");
8375 this.maskEl.left.enableDisplayMode("block");
8376 this.maskEl.bottom.enableDisplayMode("block");
8377 this.maskEl.right.enableDisplayMode("block");
8379 this.toolTip = new Roo.bootstrap.Tooltip({
8380 cls : 'roo-form-error-popover',
8382 'left' : ['r-l', [-2,0], 'right'],
8383 'right' : ['l-r', [2,0], 'left'],
8384 'bottom' : ['tl-bl', [0,2], 'top'],
8385 'top' : [ 'bl-tl', [0,-2], 'bottom']
8389 this.toolTip.render(Roo.get(document.body));
8391 this.toolTip.el.enableDisplayMode("block");
8393 Roo.get(document.body).on('click', function(){
8397 Roo.get(document.body).on('touchstart', function(){
8401 this.isApplied = true
8404 mask : function(form, target)
8408 this.target = target;
8410 if(!this.form.errorMask || !target.el){
8414 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8416 Roo.log(scrollable);
8418 var ot = this.target.el.calcOffsetsTo(scrollable);
8420 var scrollTo = ot[1] - this.form.maskOffset;
8422 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8424 scrollable.scrollTo('top', scrollTo);
8426 var box = this.target.el.getBox();
8428 var zIndex = Roo.bootstrap.Modal.zIndex++;
8431 this.maskEl.top.setStyle('position', 'absolute');
8432 this.maskEl.top.setStyle('z-index', zIndex);
8433 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8434 this.maskEl.top.setLeft(0);
8435 this.maskEl.top.setTop(0);
8436 this.maskEl.top.show();
8438 this.maskEl.left.setStyle('position', 'absolute');
8439 this.maskEl.left.setStyle('z-index', zIndex);
8440 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8441 this.maskEl.left.setLeft(0);
8442 this.maskEl.left.setTop(box.y - this.padding);
8443 this.maskEl.left.show();
8445 this.maskEl.bottom.setStyle('position', 'absolute');
8446 this.maskEl.bottom.setStyle('z-index', zIndex);
8447 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8448 this.maskEl.bottom.setLeft(0);
8449 this.maskEl.bottom.setTop(box.bottom + this.padding);
8450 this.maskEl.bottom.show();
8452 this.maskEl.right.setStyle('position', 'absolute');
8453 this.maskEl.right.setStyle('z-index', zIndex);
8454 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8455 this.maskEl.right.setLeft(box.right + this.padding);
8456 this.maskEl.right.setTop(box.y - this.padding);
8457 this.maskEl.right.show();
8459 this.toolTip.bindEl = this.target.el;
8461 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8463 var tip = this.target.blankText;
8465 if(this.target.getValue() !== '' ) {
8467 if (this.target.invalidText.length) {
8468 tip = this.target.invalidText;
8469 } else if (this.target.regexText.length){
8470 tip = this.target.regexText;
8474 this.toolTip.show(tip);
8476 this.intervalID = window.setInterval(function() {
8477 Roo.bootstrap.Form.popover.unmask();
8480 window.onwheel = function(){ return false;};
8482 (function(){ this.isMasked = true; }).defer(500, this);
8488 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8492 this.maskEl.top.setStyle('position', 'absolute');
8493 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8494 this.maskEl.top.hide();
8496 this.maskEl.left.setStyle('position', 'absolute');
8497 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8498 this.maskEl.left.hide();
8500 this.maskEl.bottom.setStyle('position', 'absolute');
8501 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8502 this.maskEl.bottom.hide();
8504 this.maskEl.right.setStyle('position', 'absolute');
8505 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8506 this.maskEl.right.hide();
8508 this.toolTip.hide();
8510 this.toolTip.el.hide();
8512 window.onwheel = function(){ return true;};
8514 if(this.intervalID){
8515 window.clearInterval(this.intervalID);
8516 this.intervalID = false;
8519 this.isMasked = false;
8529 * Ext JS Library 1.1.1
8530 * Copyright(c) 2006-2007, Ext JS, LLC.
8532 * Originally Released Under LGPL - original licence link has changed is not relivant.
8535 * <script type="text/javascript">
8538 * @class Roo.form.VTypes
8539 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8542 Roo.form.VTypes = function(){
8543 // closure these in so they are only created once.
8544 var alpha = /^[a-zA-Z_]+$/;
8545 var alphanum = /^[a-zA-Z0-9_]+$/;
8546 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8547 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8549 // All these messages and functions are configurable
8552 * The function used to validate email addresses
8553 * @param {String} value The email address
8555 'email' : function(v){
8556 return email.test(v);
8559 * The error text to display when the email validation function returns false
8562 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8564 * The keystroke filter mask to be applied on email input
8567 'emailMask' : /[a-z0-9_\.\-@]/i,
8570 * The function used to validate URLs
8571 * @param {String} value The URL
8573 'url' : function(v){
8577 * The error text to display when the url validation function returns false
8580 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8583 * The function used to validate alpha values
8584 * @param {String} value The value
8586 'alpha' : function(v){
8587 return alpha.test(v);
8590 * The error text to display when the alpha validation function returns false
8593 'alphaText' : 'This field should only contain letters and _',
8595 * The keystroke filter mask to be applied on alpha input
8598 'alphaMask' : /[a-z_]/i,
8601 * The function used to validate alphanumeric values
8602 * @param {String} value The value
8604 'alphanum' : function(v){
8605 return alphanum.test(v);
8608 * The error text to display when the alphanumeric validation function returns false
8611 'alphanumText' : 'This field should only contain letters, numbers and _',
8613 * The keystroke filter mask to be applied on alphanumeric input
8616 'alphanumMask' : /[a-z0-9_]/i
8626 * @class Roo.bootstrap.Input
8627 * @extends Roo.bootstrap.Component
8628 * Bootstrap Input class
8629 * @cfg {Boolean} disabled is it disabled
8630 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8631 * @cfg {String} name name of the input
8632 * @cfg {string} fieldLabel - the label associated
8633 * @cfg {string} placeholder - placeholder to put in text.
8634 * @cfg {string} before - input group add on before
8635 * @cfg {string} after - input group add on after
8636 * @cfg {string} size - (lg|sm) or leave empty..
8637 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8638 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8639 * @cfg {Number} md colspan out of 12 for computer-sized screens
8640 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8641 * @cfg {string} value default value of the input
8642 * @cfg {Number} labelWidth set the width of label
8643 * @cfg {Number} labellg set the width of label (1-12)
8644 * @cfg {Number} labelmd set the width of label (1-12)
8645 * @cfg {Number} labelsm set the width of label (1-12)
8646 * @cfg {Number} labelxs set the width of label (1-12)
8647 * @cfg {String} labelAlign (top|left)
8648 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8649 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8650 * @cfg {String} indicatorpos (left|right) default left
8651 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8652 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8654 * @cfg {String} align (left|center|right) Default left
8655 * @cfg {Boolean} forceFeedback (true|false) Default false
8658 * Create a new Input
8659 * @param {Object} config The config object
8662 Roo.bootstrap.Input = function(config){
8664 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8669 * Fires when this field receives input focus.
8670 * @param {Roo.form.Field} this
8675 * Fires when this field loses input focus.
8676 * @param {Roo.form.Field} this
8681 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8682 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8683 * @param {Roo.form.Field} this
8684 * @param {Roo.EventObject} e The event object
8689 * Fires just before the field blurs if the field value has changed.
8690 * @param {Roo.form.Field} this
8691 * @param {Mixed} newValue The new value
8692 * @param {Mixed} oldValue The original value
8697 * Fires after the field has been marked as invalid.
8698 * @param {Roo.form.Field} this
8699 * @param {String} msg The validation message
8704 * Fires after the field has been validated with no errors.
8705 * @param {Roo.form.Field} this
8710 * Fires after the key up
8711 * @param {Roo.form.Field} this
8712 * @param {Roo.EventObject} e The event Object
8718 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8720 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8721 automatic validation (defaults to "keyup").
8723 validationEvent : "keyup",
8725 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8727 validateOnBlur : true,
8729 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8731 validationDelay : 250,
8733 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8735 focusClass : "x-form-focus", // not needed???
8739 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8741 invalidClass : "has-warning",
8744 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8746 validClass : "has-success",
8749 * @cfg {Boolean} hasFeedback (true|false) default true
8754 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8756 invalidFeedbackClass : "glyphicon-warning-sign",
8759 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8761 validFeedbackClass : "glyphicon-ok",
8764 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8766 selectOnFocus : false,
8769 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8773 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8778 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8780 disableKeyFilter : false,
8783 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8787 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8791 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8793 blankText : "Please complete this mandatory field",
8796 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8800 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8802 maxLength : Number.MAX_VALUE,
8804 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8806 minLengthText : "The minimum length for this field is {0}",
8808 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8810 maxLengthText : "The maximum length for this field is {0}",
8814 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8815 * If available, this function will be called only after the basic validators all return true, and will be passed the
8816 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8820 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8821 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8822 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8826 * @cfg {String} regexText -- Depricated - use Invalid Text
8831 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8837 autocomplete: false,
8856 formatedValue : false,
8857 forceFeedback : false,
8859 indicatorpos : 'left',
8869 parentLabelAlign : function()
8872 while (parent.parent()) {
8873 parent = parent.parent();
8874 if (typeof(parent.labelAlign) !='undefined') {
8875 return parent.labelAlign;
8882 getAutoCreate : function()
8884 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8890 if(this.inputType != 'hidden'){
8891 cfg.cls = 'form-group' //input-group
8897 type : this.inputType,
8899 cls : 'form-control',
8900 placeholder : this.placeholder || '',
8901 autocomplete : this.autocomplete || 'new-password'
8904 if(this.capture.length){
8905 input.capture = this.capture;
8908 if(this.accept.length){
8909 input.accept = this.accept + "/*";
8913 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8916 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8917 input.maxLength = this.maxLength;
8920 if (this.disabled) {
8921 input.disabled=true;
8924 if (this.readOnly) {
8925 input.readonly=true;
8929 input.name = this.name;
8933 input.cls += ' input-' + this.size;
8937 ['xs','sm','md','lg'].map(function(size){
8938 if (settings[size]) {
8939 cfg.cls += ' col-' + size + '-' + settings[size];
8943 var inputblock = input;
8947 cls: 'glyphicon form-control-feedback'
8950 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8953 cls : 'has-feedback',
8961 if (this.before || this.after) {
8964 cls : 'input-group',
8968 if (this.before && typeof(this.before) == 'string') {
8970 inputblock.cn.push({
8972 cls : 'roo-input-before input-group-addon',
8976 if (this.before && typeof(this.before) == 'object') {
8977 this.before = Roo.factory(this.before);
8979 inputblock.cn.push({
8981 cls : 'roo-input-before input-group-' +
8982 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8986 inputblock.cn.push(input);
8988 if (this.after && typeof(this.after) == 'string') {
8989 inputblock.cn.push({
8991 cls : 'roo-input-after input-group-addon',
8995 if (this.after && typeof(this.after) == 'object') {
8996 this.after = Roo.factory(this.after);
8998 inputblock.cn.push({
9000 cls : 'roo-input-after input-group-' +
9001 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9005 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9006 inputblock.cls += ' has-feedback';
9007 inputblock.cn.push(feedback);
9011 if (align ==='left' && this.fieldLabel.length) {
9013 cfg.cls += ' roo-form-group-label-left';
9018 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9019 tooltip : 'This field is required'
9024 cls : 'control-label',
9025 html : this.fieldLabel
9036 var labelCfg = cfg.cn[1];
9037 var contentCfg = cfg.cn[2];
9039 if(this.indicatorpos == 'right'){
9044 cls : 'control-label',
9048 html : this.fieldLabel
9052 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9053 tooltip : 'This field is required'
9066 labelCfg = cfg.cn[0];
9067 contentCfg = cfg.cn[1];
9071 if(this.labelWidth > 12){
9072 labelCfg.style = "width: " + this.labelWidth + 'px';
9075 if(this.labelWidth < 13 && this.labelmd == 0){
9076 this.labelmd = this.labelWidth;
9079 if(this.labellg > 0){
9080 labelCfg.cls += ' col-lg-' + this.labellg;
9081 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9084 if(this.labelmd > 0){
9085 labelCfg.cls += ' col-md-' + this.labelmd;
9086 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9089 if(this.labelsm > 0){
9090 labelCfg.cls += ' col-sm-' + this.labelsm;
9091 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9094 if(this.labelxs > 0){
9095 labelCfg.cls += ' col-xs-' + this.labelxs;
9096 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9100 } else if ( this.fieldLabel.length) {
9105 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9106 tooltip : 'This field is required'
9110 //cls : 'input-group-addon',
9111 html : this.fieldLabel
9119 if(this.indicatorpos == 'right'){
9124 //cls : 'input-group-addon',
9125 html : this.fieldLabel
9130 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9131 tooltip : 'This field is required'
9151 if (this.parentType === 'Navbar' && this.parent().bar) {
9152 cfg.cls += ' navbar-form';
9155 if (this.parentType === 'NavGroup') {
9156 cfg.cls += ' navbar-form';
9164 * return the real input element.
9166 inputEl: function ()
9168 return this.el.select('input.form-control',true).first();
9171 tooltipEl : function()
9173 return this.inputEl();
9176 indicatorEl : function()
9178 var indicator = this.el.select('i.roo-required-indicator',true).first();
9188 setDisabled : function(v)
9190 var i = this.inputEl().dom;
9192 i.removeAttribute('disabled');
9196 i.setAttribute('disabled','true');
9198 initEvents : function()
9201 this.inputEl().on("keydown" , this.fireKey, this);
9202 this.inputEl().on("focus", this.onFocus, this);
9203 this.inputEl().on("blur", this.onBlur, this);
9205 this.inputEl().relayEvent('keyup', this);
9207 this.indicator = this.indicatorEl();
9210 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9213 // reference to original value for reset
9214 this.originalValue = this.getValue();
9215 //Roo.form.TextField.superclass.initEvents.call(this);
9216 if(this.validationEvent == 'keyup'){
9217 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9218 this.inputEl().on('keyup', this.filterValidation, this);
9220 else if(this.validationEvent !== false){
9221 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9224 if(this.selectOnFocus){
9225 this.on("focus", this.preFocus, this);
9228 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9229 this.inputEl().on("keypress", this.filterKeys, this);
9231 this.inputEl().relayEvent('keypress', this);
9234 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9235 this.el.on("click", this.autoSize, this);
9238 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9239 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9242 if (typeof(this.before) == 'object') {
9243 this.before.render(this.el.select('.roo-input-before',true).first());
9245 if (typeof(this.after) == 'object') {
9246 this.after.render(this.el.select('.roo-input-after',true).first());
9249 this.inputEl().on('change', this.onChange, this);
9252 filterValidation : function(e){
9253 if(!e.isNavKeyPress()){
9254 this.validationTask.delay(this.validationDelay);
9258 * Validates the field value
9259 * @return {Boolean} True if the value is valid, else false
9261 validate : function(){
9262 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9263 if(this.disabled || this.validateValue(this.getRawValue())){
9274 * Validates a value according to the field's validation rules and marks the field as invalid
9275 * if the validation fails
9276 * @param {Mixed} value The value to validate
9277 * @return {Boolean} True if the value is valid, else false
9279 validateValue : function(value)
9281 if(this.getVisibilityEl().hasClass('hidden')){
9285 if(value.length < 1) { // if it's blank
9286 if(this.allowBlank){
9292 if(value.length < this.minLength){
9295 if(value.length > this.maxLength){
9299 var vt = Roo.form.VTypes;
9300 if(!vt[this.vtype](value, this)){
9304 if(typeof this.validator == "function"){
9305 var msg = this.validator(value);
9309 if (typeof(msg) == 'string') {
9310 this.invalidText = msg;
9314 if(this.regex && !this.regex.test(value)){
9322 fireKey : function(e){
9323 //Roo.log('field ' + e.getKey());
9324 if(e.isNavKeyPress()){
9325 this.fireEvent("specialkey", this, e);
9328 focus : function (selectText){
9330 this.inputEl().focus();
9331 if(selectText === true){
9332 this.inputEl().dom.select();
9338 onFocus : function(){
9339 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9340 // this.el.addClass(this.focusClass);
9343 this.hasFocus = true;
9344 this.startValue = this.getValue();
9345 this.fireEvent("focus", this);
9349 beforeBlur : Roo.emptyFn,
9353 onBlur : function(){
9355 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9356 //this.el.removeClass(this.focusClass);
9358 this.hasFocus = false;
9359 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9362 var v = this.getValue();
9363 if(String(v) !== String(this.startValue)){
9364 this.fireEvent('change', this, v, this.startValue);
9366 this.fireEvent("blur", this);
9369 onChange : function(e)
9371 var v = this.getValue();
9372 if(String(v) !== String(this.startValue)){
9373 this.fireEvent('change', this, v, this.startValue);
9379 * Resets the current field value to the originally loaded value and clears any validation messages
9382 this.setValue(this.originalValue);
9386 * Returns the name of the field
9387 * @return {Mixed} name The name field
9389 getName: function(){
9393 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9394 * @return {Mixed} value The field value
9396 getValue : function(){
9398 var v = this.inputEl().getValue();
9403 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9404 * @return {Mixed} value The field value
9406 getRawValue : function(){
9407 var v = this.inputEl().getValue();
9413 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9414 * @param {Mixed} value The value to set
9416 setRawValue : function(v){
9417 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9420 selectText : function(start, end){
9421 var v = this.getRawValue();
9423 start = start === undefined ? 0 : start;
9424 end = end === undefined ? v.length : end;
9425 var d = this.inputEl().dom;
9426 if(d.setSelectionRange){
9427 d.setSelectionRange(start, end);
9428 }else if(d.createTextRange){
9429 var range = d.createTextRange();
9430 range.moveStart("character", start);
9431 range.moveEnd("character", v.length-end);
9438 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9439 * @param {Mixed} value The value to set
9441 setValue : function(v){
9444 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9450 processValue : function(value){
9451 if(this.stripCharsRe){
9452 var newValue = value.replace(this.stripCharsRe, '');
9453 if(newValue !== value){
9454 this.setRawValue(newValue);
9461 preFocus : function(){
9463 if(this.selectOnFocus){
9464 this.inputEl().dom.select();
9467 filterKeys : function(e){
9469 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9472 var c = e.getCharCode(), cc = String.fromCharCode(c);
9473 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9476 if(!this.maskRe.test(cc)){
9481 * Clear any invalid styles/messages for this field
9483 clearInvalid : function(){
9485 if(!this.el || this.preventMark){ // not rendered
9490 this.el.removeClass(this.invalidClass);
9492 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9494 var feedback = this.el.select('.form-control-feedback', true).first();
9497 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9503 this.indicator.removeClass('visible');
9504 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9507 this.fireEvent('valid', this);
9511 * Mark this field as valid
9513 markValid : function()
9515 if(!this.el || this.preventMark){ // not rendered...
9519 this.el.removeClass([this.invalidClass, this.validClass]);
9521 var feedback = this.el.select('.form-control-feedback', true).first();
9524 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9528 this.indicator.removeClass('visible');
9529 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9536 if(this.allowBlank && !this.getRawValue().length){
9540 this.el.addClass(this.validClass);
9542 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9544 var feedback = this.el.select('.form-control-feedback', true).first();
9547 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9548 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9553 this.fireEvent('valid', this);
9557 * Mark this field as invalid
9558 * @param {String} msg The validation message
9560 markInvalid : function(msg)
9562 if(!this.el || this.preventMark){ // not rendered
9566 this.el.removeClass([this.invalidClass, this.validClass]);
9568 var feedback = this.el.select('.form-control-feedback', true).first();
9571 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9578 if(this.allowBlank && !this.getRawValue().length){
9583 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9584 this.indicator.addClass('visible');
9587 this.el.addClass(this.invalidClass);
9589 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9591 var feedback = this.el.select('.form-control-feedback', true).first();
9594 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9596 if(this.getValue().length || this.forceFeedback){
9597 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9604 this.fireEvent('invalid', this, msg);
9607 SafariOnKeyDown : function(event)
9609 // this is a workaround for a password hang bug on chrome/ webkit.
9610 if (this.inputEl().dom.type != 'password') {
9614 var isSelectAll = false;
9616 if(this.inputEl().dom.selectionEnd > 0){
9617 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9619 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9620 event.preventDefault();
9625 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9627 event.preventDefault();
9628 // this is very hacky as keydown always get's upper case.
9630 var cc = String.fromCharCode(event.getCharCode());
9631 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9635 adjustWidth : function(tag, w){
9636 tag = tag.toLowerCase();
9637 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9638 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9642 if(tag == 'textarea'){
9645 }else if(Roo.isOpera){
9649 if(tag == 'textarea'){
9657 setFieldLabel : function(v)
9664 var ar = this.el.select('label > span',true);
9666 if (ar.elements.length) {
9667 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9668 this.fieldLabel = v;
9672 var br = this.el.select('label',true);
9674 if(br.elements.length) {
9675 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9676 this.fieldLabel = v;
9680 Roo.log('Cannot Found any of label > span || label in input');
9684 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9685 this.fieldLabel = v;
9700 * @class Roo.bootstrap.TextArea
9701 * @extends Roo.bootstrap.Input
9702 * Bootstrap TextArea class
9703 * @cfg {Number} cols Specifies the visible width of a text area
9704 * @cfg {Number} rows Specifies the visible number of lines in a text area
9705 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9706 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9707 * @cfg {string} html text
9710 * Create a new TextArea
9711 * @param {Object} config The config object
9714 Roo.bootstrap.TextArea = function(config){
9715 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9719 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9729 getAutoCreate : function(){
9731 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9737 if(this.inputType != 'hidden'){
9738 cfg.cls = 'form-group' //input-group
9746 value : this.value || '',
9747 html: this.html || '',
9748 cls : 'form-control',
9749 placeholder : this.placeholder || ''
9753 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9754 input.maxLength = this.maxLength;
9758 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9762 input.cols = this.cols;
9765 if (this.readOnly) {
9766 input.readonly = true;
9770 input.name = this.name;
9774 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9778 ['xs','sm','md','lg'].map(function(size){
9779 if (settings[size]) {
9780 cfg.cls += ' col-' + size + '-' + settings[size];
9784 var inputblock = input;
9786 if(this.hasFeedback && !this.allowBlank){
9790 cls: 'glyphicon form-control-feedback'
9794 cls : 'has-feedback',
9803 if (this.before || this.after) {
9806 cls : 'input-group',
9810 inputblock.cn.push({
9812 cls : 'input-group-addon',
9817 inputblock.cn.push(input);
9819 if(this.hasFeedback && !this.allowBlank){
9820 inputblock.cls += ' has-feedback';
9821 inputblock.cn.push(feedback);
9825 inputblock.cn.push({
9827 cls : 'input-group-addon',
9834 if (align ==='left' && this.fieldLabel.length) {
9839 cls : 'control-label',
9840 html : this.fieldLabel
9851 if(this.labelWidth > 12){
9852 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9855 if(this.labelWidth < 13 && this.labelmd == 0){
9856 this.labelmd = this.labelWidth;
9859 if(this.labellg > 0){
9860 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9861 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9864 if(this.labelmd > 0){
9865 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9866 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9869 if(this.labelsm > 0){
9870 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9871 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9874 if(this.labelxs > 0){
9875 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9876 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9879 } else if ( this.fieldLabel.length) {
9884 //cls : 'input-group-addon',
9885 html : this.fieldLabel
9903 if (this.disabled) {
9904 input.disabled=true;
9911 * return the real textarea element.
9913 inputEl: function ()
9915 return this.el.select('textarea.form-control',true).first();
9919 * Clear any invalid styles/messages for this field
9921 clearInvalid : function()
9924 if(!this.el || this.preventMark){ // not rendered
9928 var label = this.el.select('label', true).first();
9929 var icon = this.el.select('i.fa-star', true).first();
9935 this.el.removeClass(this.invalidClass);
9937 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9939 var feedback = this.el.select('.form-control-feedback', true).first();
9942 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9947 this.fireEvent('valid', this);
9951 * Mark this field as valid
9953 markValid : function()
9955 if(!this.el || this.preventMark){ // not rendered
9959 this.el.removeClass([this.invalidClass, this.validClass]);
9961 var feedback = this.el.select('.form-control-feedback', true).first();
9964 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9967 if(this.disabled || this.allowBlank){
9971 var label = this.el.select('label', true).first();
9972 var icon = this.el.select('i.fa-star', true).first();
9978 this.el.addClass(this.validClass);
9980 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9982 var feedback = this.el.select('.form-control-feedback', true).first();
9985 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9986 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9991 this.fireEvent('valid', this);
9995 * Mark this field as invalid
9996 * @param {String} msg The validation message
9998 markInvalid : function(msg)
10000 if(!this.el || this.preventMark){ // not rendered
10004 this.el.removeClass([this.invalidClass, this.validClass]);
10006 var feedback = this.el.select('.form-control-feedback', true).first();
10009 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10012 if(this.disabled || this.allowBlank){
10016 var label = this.el.select('label', true).first();
10017 var icon = this.el.select('i.fa-star', true).first();
10019 if(!this.getValue().length && label && !icon){
10020 this.el.createChild({
10022 cls : 'text-danger fa fa-lg fa-star',
10023 tooltip : 'This field is required',
10024 style : 'margin-right:5px;'
10028 this.el.addClass(this.invalidClass);
10030 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10032 var feedback = this.el.select('.form-control-feedback', true).first();
10035 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10037 if(this.getValue().length || this.forceFeedback){
10038 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10045 this.fireEvent('invalid', this, msg);
10053 * trigger field - base class for combo..
10058 * @class Roo.bootstrap.TriggerField
10059 * @extends Roo.bootstrap.Input
10060 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10061 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10062 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10063 * for which you can provide a custom implementation. For example:
10065 var trigger = new Roo.bootstrap.TriggerField();
10066 trigger.onTriggerClick = myTriggerFn;
10067 trigger.applyTo('my-field');
10070 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10071 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10072 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10073 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10074 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10077 * Create a new TriggerField.
10078 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10079 * to the base TextField)
10081 Roo.bootstrap.TriggerField = function(config){
10082 this.mimicing = false;
10083 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10086 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10088 * @cfg {String} triggerClass A CSS class to apply to the trigger
10091 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10096 * @cfg {Boolean} removable (true|false) special filter default false
10100 /** @cfg {Boolean} grow @hide */
10101 /** @cfg {Number} growMin @hide */
10102 /** @cfg {Number} growMax @hide */
10108 autoSize: Roo.emptyFn,
10112 deferHeight : true,
10115 actionMode : 'wrap',
10120 getAutoCreate : function(){
10122 var align = this.labelAlign || this.parentLabelAlign();
10127 cls: 'form-group' //input-group
10134 type : this.inputType,
10135 cls : 'form-control',
10136 autocomplete: 'new-password',
10137 placeholder : this.placeholder || ''
10141 input.name = this.name;
10144 input.cls += ' input-' + this.size;
10147 if (this.disabled) {
10148 input.disabled=true;
10151 var inputblock = input;
10153 if(this.hasFeedback && !this.allowBlank){
10157 cls: 'glyphicon form-control-feedback'
10160 if(this.removable && !this.editable && !this.tickable){
10162 cls : 'has-feedback',
10168 cls : 'roo-combo-removable-btn close'
10175 cls : 'has-feedback',
10184 if(this.removable && !this.editable && !this.tickable){
10186 cls : 'roo-removable',
10192 cls : 'roo-combo-removable-btn close'
10199 if (this.before || this.after) {
10202 cls : 'input-group',
10206 inputblock.cn.push({
10208 cls : 'input-group-addon',
10213 inputblock.cn.push(input);
10215 if(this.hasFeedback && !this.allowBlank){
10216 inputblock.cls += ' has-feedback';
10217 inputblock.cn.push(feedback);
10221 inputblock.cn.push({
10223 cls : 'input-group-addon',
10236 cls: 'form-hidden-field'
10250 cls: 'form-hidden-field'
10254 cls: 'roo-select2-choices',
10258 cls: 'roo-select2-search-field',
10271 cls: 'roo-select2-container input-group',
10276 // cls: 'typeahead typeahead-long dropdown-menu',
10277 // style: 'display:none'
10282 if(!this.multiple && this.showToggleBtn){
10288 if (this.caret != false) {
10291 cls: 'fa fa-' + this.caret
10298 cls : 'input-group-addon btn dropdown-toggle',
10303 cls: 'combobox-clear',
10317 combobox.cls += ' roo-select2-container-multi';
10320 if (align ==='left' && this.fieldLabel.length) {
10322 cfg.cls += ' roo-form-group-label-left';
10327 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10328 tooltip : 'This field is required'
10333 cls : 'control-label',
10334 html : this.fieldLabel
10346 var labelCfg = cfg.cn[1];
10347 var contentCfg = cfg.cn[2];
10349 if(this.indicatorpos == 'right'){
10354 cls : 'control-label',
10358 html : this.fieldLabel
10362 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10363 tooltip : 'This field is required'
10376 labelCfg = cfg.cn[0];
10377 contentCfg = cfg.cn[1];
10380 if(this.labelWidth > 12){
10381 labelCfg.style = "width: " + this.labelWidth + 'px';
10384 if(this.labelWidth < 13 && this.labelmd == 0){
10385 this.labelmd = this.labelWidth;
10388 if(this.labellg > 0){
10389 labelCfg.cls += ' col-lg-' + this.labellg;
10390 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10393 if(this.labelmd > 0){
10394 labelCfg.cls += ' col-md-' + this.labelmd;
10395 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10398 if(this.labelsm > 0){
10399 labelCfg.cls += ' col-sm-' + this.labelsm;
10400 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10403 if(this.labelxs > 0){
10404 labelCfg.cls += ' col-xs-' + this.labelxs;
10405 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10408 } else if ( this.fieldLabel.length) {
10409 // Roo.log(" label");
10413 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10414 tooltip : 'This field is required'
10418 //cls : 'input-group-addon',
10419 html : this.fieldLabel
10427 if(this.indicatorpos == 'right'){
10435 html : this.fieldLabel
10439 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10440 tooltip : 'This field is required'
10453 // Roo.log(" no label && no align");
10460 ['xs','sm','md','lg'].map(function(size){
10461 if (settings[size]) {
10462 cfg.cls += ' col-' + size + '-' + settings[size];
10473 onResize : function(w, h){
10474 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10475 // if(typeof w == 'number'){
10476 // var x = w - this.trigger.getWidth();
10477 // this.inputEl().setWidth(this.adjustWidth('input', x));
10478 // this.trigger.setStyle('left', x+'px');
10483 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10486 getResizeEl : function(){
10487 return this.inputEl();
10491 getPositionEl : function(){
10492 return this.inputEl();
10496 alignErrorIcon : function(){
10497 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10501 initEvents : function(){
10505 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10506 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10507 if(!this.multiple && this.showToggleBtn){
10508 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10509 if(this.hideTrigger){
10510 this.trigger.setDisplayed(false);
10512 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10516 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10519 if(this.removable && !this.editable && !this.tickable){
10520 var close = this.closeTriggerEl();
10523 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10524 close.on('click', this.removeBtnClick, this, close);
10528 //this.trigger.addClassOnOver('x-form-trigger-over');
10529 //this.trigger.addClassOnClick('x-form-trigger-click');
10532 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10536 closeTriggerEl : function()
10538 var close = this.el.select('.roo-combo-removable-btn', true).first();
10539 return close ? close : false;
10542 removeBtnClick : function(e, h, el)
10544 e.preventDefault();
10546 if(this.fireEvent("remove", this) !== false){
10548 this.fireEvent("afterremove", this)
10552 createList : function()
10554 this.list = Roo.get(document.body).createChild({
10556 cls: 'typeahead typeahead-long dropdown-menu',
10557 style: 'display:none'
10560 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10565 initTrigger : function(){
10570 onDestroy : function(){
10572 this.trigger.removeAllListeners();
10573 // this.trigger.remove();
10576 // this.wrap.remove();
10578 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10582 onFocus : function(){
10583 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10585 if(!this.mimicing){
10586 this.wrap.addClass('x-trigger-wrap-focus');
10587 this.mimicing = true;
10588 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10589 if(this.monitorTab){
10590 this.el.on("keydown", this.checkTab, this);
10597 checkTab : function(e){
10598 if(e.getKey() == e.TAB){
10599 this.triggerBlur();
10604 onBlur : function(){
10609 mimicBlur : function(e, t){
10611 if(!this.wrap.contains(t) && this.validateBlur()){
10612 this.triggerBlur();
10618 triggerBlur : function(){
10619 this.mimicing = false;
10620 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10621 if(this.monitorTab){
10622 this.el.un("keydown", this.checkTab, this);
10624 //this.wrap.removeClass('x-trigger-wrap-focus');
10625 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10629 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10630 validateBlur : function(e, t){
10635 onDisable : function(){
10636 this.inputEl().dom.disabled = true;
10637 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10639 // this.wrap.addClass('x-item-disabled');
10644 onEnable : function(){
10645 this.inputEl().dom.disabled = false;
10646 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10648 // this.el.removeClass('x-item-disabled');
10653 onShow : function(){
10654 var ae = this.getActionEl();
10657 ae.dom.style.display = '';
10658 ae.dom.style.visibility = 'visible';
10664 onHide : function(){
10665 var ae = this.getActionEl();
10666 ae.dom.style.display = 'none';
10670 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10671 * by an implementing function.
10673 * @param {EventObject} e
10675 onTriggerClick : Roo.emptyFn
10679 * Ext JS Library 1.1.1
10680 * Copyright(c) 2006-2007, Ext JS, LLC.
10682 * Originally Released Under LGPL - original licence link has changed is not relivant.
10685 * <script type="text/javascript">
10690 * @class Roo.data.SortTypes
10692 * Defines the default sorting (casting?) comparison functions used when sorting data.
10694 Roo.data.SortTypes = {
10696 * Default sort that does nothing
10697 * @param {Mixed} s The value being converted
10698 * @return {Mixed} The comparison value
10700 none : function(s){
10705 * The regular expression used to strip tags
10709 stripTagsRE : /<\/?[^>]+>/gi,
10712 * Strips all HTML tags to sort on text only
10713 * @param {Mixed} s The value being converted
10714 * @return {String} The comparison value
10716 asText : function(s){
10717 return String(s).replace(this.stripTagsRE, "");
10721 * Strips all HTML tags to sort on text only - Case insensitive
10722 * @param {Mixed} s The value being converted
10723 * @return {String} The comparison value
10725 asUCText : function(s){
10726 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10730 * Case insensitive string
10731 * @param {Mixed} s The value being converted
10732 * @return {String} The comparison value
10734 asUCString : function(s) {
10735 return String(s).toUpperCase();
10740 * @param {Mixed} s The value being converted
10741 * @return {Number} The comparison value
10743 asDate : function(s) {
10747 if(s instanceof Date){
10748 return s.getTime();
10750 return Date.parse(String(s));
10755 * @param {Mixed} s The value being converted
10756 * @return {Float} The comparison value
10758 asFloat : function(s) {
10759 var val = parseFloat(String(s).replace(/,/g, ""));
10768 * @param {Mixed} s The value being converted
10769 * @return {Number} The comparison value
10771 asInt : function(s) {
10772 var val = parseInt(String(s).replace(/,/g, ""));
10780 * Ext JS Library 1.1.1
10781 * Copyright(c) 2006-2007, Ext JS, LLC.
10783 * Originally Released Under LGPL - original licence link has changed is not relivant.
10786 * <script type="text/javascript">
10790 * @class Roo.data.Record
10791 * Instances of this class encapsulate both record <em>definition</em> information, and record
10792 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10793 * to access Records cached in an {@link Roo.data.Store} object.<br>
10795 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10796 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10799 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10801 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10802 * {@link #create}. The parameters are the same.
10803 * @param {Array} data An associative Array of data values keyed by the field name.
10804 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10805 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10806 * not specified an integer id is generated.
10808 Roo.data.Record = function(data, id){
10809 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10814 * Generate a constructor for a specific record layout.
10815 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10816 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10817 * Each field definition object may contain the following properties: <ul>
10818 * <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,
10819 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10820 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10821 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10822 * is being used, then this is a string containing the javascript expression to reference the data relative to
10823 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10824 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10825 * this may be omitted.</p></li>
10826 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10827 * <ul><li>auto (Default, implies no conversion)</li>
10832 * <li>date</li></ul></p></li>
10833 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10834 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10835 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10836 * by the Reader into an object that will be stored in the Record. It is passed the
10837 * following parameters:<ul>
10838 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10840 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10842 * <br>usage:<br><pre><code>
10843 var TopicRecord = Roo.data.Record.create(
10844 {name: 'title', mapping: 'topic_title'},
10845 {name: 'author', mapping: 'username'},
10846 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10847 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10848 {name: 'lastPoster', mapping: 'user2'},
10849 {name: 'excerpt', mapping: 'post_text'}
10852 var myNewRecord = new TopicRecord({
10853 title: 'Do my job please',
10856 lastPost: new Date(),
10857 lastPoster: 'Animal',
10858 excerpt: 'No way dude!'
10860 myStore.add(myNewRecord);
10865 Roo.data.Record.create = function(o){
10866 var f = function(){
10867 f.superclass.constructor.apply(this, arguments);
10869 Roo.extend(f, Roo.data.Record);
10870 var p = f.prototype;
10871 p.fields = new Roo.util.MixedCollection(false, function(field){
10874 for(var i = 0, len = o.length; i < len; i++){
10875 p.fields.add(new Roo.data.Field(o[i]));
10877 f.getField = function(name){
10878 return p.fields.get(name);
10883 Roo.data.Record.AUTO_ID = 1000;
10884 Roo.data.Record.EDIT = 'edit';
10885 Roo.data.Record.REJECT = 'reject';
10886 Roo.data.Record.COMMIT = 'commit';
10888 Roo.data.Record.prototype = {
10890 * Readonly flag - true if this record has been modified.
10899 join : function(store){
10900 this.store = store;
10904 * Set the named field to the specified value.
10905 * @param {String} name The name of the field to set.
10906 * @param {Object} value The value to set the field to.
10908 set : function(name, value){
10909 if(this.data[name] == value){
10913 if(!this.modified){
10914 this.modified = {};
10916 if(typeof this.modified[name] == 'undefined'){
10917 this.modified[name] = this.data[name];
10919 this.data[name] = value;
10920 if(!this.editing && this.store){
10921 this.store.afterEdit(this);
10926 * Get the value of the named field.
10927 * @param {String} name The name of the field to get the value of.
10928 * @return {Object} The value of the field.
10930 get : function(name){
10931 return this.data[name];
10935 beginEdit : function(){
10936 this.editing = true;
10937 this.modified = {};
10941 cancelEdit : function(){
10942 this.editing = false;
10943 delete this.modified;
10947 endEdit : function(){
10948 this.editing = false;
10949 if(this.dirty && this.store){
10950 this.store.afterEdit(this);
10955 * Usually called by the {@link Roo.data.Store} which owns the Record.
10956 * Rejects all changes made to the Record since either creation, or the last commit operation.
10957 * Modified fields are reverted to their original values.
10959 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10960 * of reject operations.
10962 reject : function(){
10963 var m = this.modified;
10965 if(typeof m[n] != "function"){
10966 this.data[n] = m[n];
10969 this.dirty = false;
10970 delete this.modified;
10971 this.editing = false;
10973 this.store.afterReject(this);
10978 * Usually called by the {@link Roo.data.Store} which owns the Record.
10979 * Commits all changes made to the Record since either creation, or the last commit operation.
10981 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10982 * of commit operations.
10984 commit : function(){
10985 this.dirty = false;
10986 delete this.modified;
10987 this.editing = false;
10989 this.store.afterCommit(this);
10994 hasError : function(){
10995 return this.error != null;
10999 clearError : function(){
11004 * Creates a copy of this record.
11005 * @param {String} id (optional) A new record id if you don't want to use this record's id
11008 copy : function(newId) {
11009 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11013 * Ext JS Library 1.1.1
11014 * Copyright(c) 2006-2007, Ext JS, LLC.
11016 * Originally Released Under LGPL - original licence link has changed is not relivant.
11019 * <script type="text/javascript">
11025 * @class Roo.data.Store
11026 * @extends Roo.util.Observable
11027 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11028 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11030 * 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
11031 * has no knowledge of the format of the data returned by the Proxy.<br>
11033 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11034 * instances from the data object. These records are cached and made available through accessor functions.
11036 * Creates a new Store.
11037 * @param {Object} config A config object containing the objects needed for the Store to access data,
11038 * and read the data into Records.
11040 Roo.data.Store = function(config){
11041 this.data = new Roo.util.MixedCollection(false);
11042 this.data.getKey = function(o){
11045 this.baseParams = {};
11047 this.paramNames = {
11052 "multisort" : "_multisort"
11055 if(config && config.data){
11056 this.inlineData = config.data;
11057 delete config.data;
11060 Roo.apply(this, config);
11062 if(this.reader){ // reader passed
11063 this.reader = Roo.factory(this.reader, Roo.data);
11064 this.reader.xmodule = this.xmodule || false;
11065 if(!this.recordType){
11066 this.recordType = this.reader.recordType;
11068 if(this.reader.onMetaChange){
11069 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11073 if(this.recordType){
11074 this.fields = this.recordType.prototype.fields;
11076 this.modified = [];
11080 * @event datachanged
11081 * Fires when the data cache has changed, and a widget which is using this Store
11082 * as a Record cache should refresh its view.
11083 * @param {Store} this
11085 datachanged : true,
11087 * @event metachange
11088 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11089 * @param {Store} this
11090 * @param {Object} meta The JSON metadata
11095 * Fires when Records have been added to the Store
11096 * @param {Store} this
11097 * @param {Roo.data.Record[]} records The array of Records added
11098 * @param {Number} index The index at which the record(s) were added
11103 * Fires when a Record has been removed from the Store
11104 * @param {Store} this
11105 * @param {Roo.data.Record} record The Record that was removed
11106 * @param {Number} index The index at which the record was removed
11111 * Fires when a Record has been updated
11112 * @param {Store} this
11113 * @param {Roo.data.Record} record The Record that was updated
11114 * @param {String} operation The update operation being performed. Value may be one of:
11116 Roo.data.Record.EDIT
11117 Roo.data.Record.REJECT
11118 Roo.data.Record.COMMIT
11124 * Fires when the data cache has been cleared.
11125 * @param {Store} this
11129 * @event beforeload
11130 * Fires before a request is made for a new data object. If the beforeload handler returns false
11131 * the load action will be canceled.
11132 * @param {Store} this
11133 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11137 * @event beforeloadadd
11138 * Fires after a new set of Records has been loaded.
11139 * @param {Store} this
11140 * @param {Roo.data.Record[]} records The Records that were loaded
11141 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11143 beforeloadadd : true,
11146 * Fires after a new set of Records has been loaded, before they are added to the store.
11147 * @param {Store} this
11148 * @param {Roo.data.Record[]} records The Records that were loaded
11149 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11150 * @params {Object} return from reader
11154 * @event loadexception
11155 * Fires if an exception occurs in the Proxy during loading.
11156 * Called with the signature of the Proxy's "loadexception" event.
11157 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11160 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11161 * @param {Object} load options
11162 * @param {Object} jsonData from your request (normally this contains the Exception)
11164 loadexception : true
11168 this.proxy = Roo.factory(this.proxy, Roo.data);
11169 this.proxy.xmodule = this.xmodule || false;
11170 this.relayEvents(this.proxy, ["loadexception"]);
11172 this.sortToggle = {};
11173 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11175 Roo.data.Store.superclass.constructor.call(this);
11177 if(this.inlineData){
11178 this.loadData(this.inlineData);
11179 delete this.inlineData;
11183 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11185 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11186 * without a remote query - used by combo/forms at present.
11190 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11193 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11196 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11197 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11200 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11201 * on any HTTP request
11204 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11207 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11211 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11212 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11214 remoteSort : false,
11217 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11218 * loaded or when a record is removed. (defaults to false).
11220 pruneModifiedRecords : false,
11223 lastOptions : null,
11226 * Add Records to the Store and fires the add event.
11227 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11229 add : function(records){
11230 records = [].concat(records);
11231 for(var i = 0, len = records.length; i < len; i++){
11232 records[i].join(this);
11234 var index = this.data.length;
11235 this.data.addAll(records);
11236 this.fireEvent("add", this, records, index);
11240 * Remove a Record from the Store and fires the remove event.
11241 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11243 remove : function(record){
11244 var index = this.data.indexOf(record);
11245 this.data.removeAt(index);
11247 if(this.pruneModifiedRecords){
11248 this.modified.remove(record);
11250 this.fireEvent("remove", this, record, index);
11254 * Remove all Records from the Store and fires the clear event.
11256 removeAll : function(){
11258 if(this.pruneModifiedRecords){
11259 this.modified = [];
11261 this.fireEvent("clear", this);
11265 * Inserts Records to the Store at the given index and fires the add event.
11266 * @param {Number} index The start index at which to insert the passed Records.
11267 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11269 insert : function(index, records){
11270 records = [].concat(records);
11271 for(var i = 0, len = records.length; i < len; i++){
11272 this.data.insert(index, records[i]);
11273 records[i].join(this);
11275 this.fireEvent("add", this, records, index);
11279 * Get the index within the cache of the passed Record.
11280 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11281 * @return {Number} The index of the passed Record. Returns -1 if not found.
11283 indexOf : function(record){
11284 return this.data.indexOf(record);
11288 * Get the index within the cache of the Record with the passed id.
11289 * @param {String} id The id of the Record to find.
11290 * @return {Number} The index of the Record. Returns -1 if not found.
11292 indexOfId : function(id){
11293 return this.data.indexOfKey(id);
11297 * Get the Record with the specified id.
11298 * @param {String} id The id of the Record to find.
11299 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11301 getById : function(id){
11302 return this.data.key(id);
11306 * Get the Record at the specified index.
11307 * @param {Number} index The index of the Record to find.
11308 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11310 getAt : function(index){
11311 return this.data.itemAt(index);
11315 * Returns a range of Records between specified indices.
11316 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11317 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11318 * @return {Roo.data.Record[]} An array of Records
11320 getRange : function(start, end){
11321 return this.data.getRange(start, end);
11325 storeOptions : function(o){
11326 o = Roo.apply({}, o);
11329 this.lastOptions = o;
11333 * Loads the Record cache from the configured Proxy using the configured Reader.
11335 * If using remote paging, then the first load call must specify the <em>start</em>
11336 * and <em>limit</em> properties in the options.params property to establish the initial
11337 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11339 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11340 * and this call will return before the new data has been loaded. Perform any post-processing
11341 * in a callback function, or in a "load" event handler.</strong>
11343 * @param {Object} options An object containing properties which control loading options:<ul>
11344 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11345 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11346 * passed the following arguments:<ul>
11347 * <li>r : Roo.data.Record[]</li>
11348 * <li>options: Options object from the load call</li>
11349 * <li>success: Boolean success indicator</li></ul></li>
11350 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11351 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11354 load : function(options){
11355 options = options || {};
11356 if(this.fireEvent("beforeload", this, options) !== false){
11357 this.storeOptions(options);
11358 var p = Roo.apply(options.params || {}, this.baseParams);
11359 // if meta was not loaded from remote source.. try requesting it.
11360 if (!this.reader.metaFromRemote) {
11361 p._requestMeta = 1;
11363 if(this.sortInfo && this.remoteSort){
11364 var pn = this.paramNames;
11365 p[pn["sort"]] = this.sortInfo.field;
11366 p[pn["dir"]] = this.sortInfo.direction;
11368 if (this.multiSort) {
11369 var pn = this.paramNames;
11370 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11373 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11378 * Reloads the Record cache from the configured Proxy using the configured Reader and
11379 * the options from the last load operation performed.
11380 * @param {Object} options (optional) An object containing properties which may override the options
11381 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11382 * the most recently used options are reused).
11384 reload : function(options){
11385 this.load(Roo.applyIf(options||{}, this.lastOptions));
11389 // Called as a callback by the Reader during a load operation.
11390 loadRecords : function(o, options, success){
11391 if(!o || success === false){
11392 if(success !== false){
11393 this.fireEvent("load", this, [], options, o);
11395 if(options.callback){
11396 options.callback.call(options.scope || this, [], options, false);
11400 // if data returned failure - throw an exception.
11401 if (o.success === false) {
11402 // show a message if no listener is registered.
11403 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11404 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11406 // loadmask wil be hooked into this..
11407 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11410 var r = o.records, t = o.totalRecords || r.length;
11412 this.fireEvent("beforeloadadd", this, r, options, o);
11414 if(!options || options.add !== true){
11415 if(this.pruneModifiedRecords){
11416 this.modified = [];
11418 for(var i = 0, len = r.length; i < len; i++){
11422 this.data = this.snapshot;
11423 delete this.snapshot;
11426 this.data.addAll(r);
11427 this.totalLength = t;
11429 this.fireEvent("datachanged", this);
11431 this.totalLength = Math.max(t, this.data.length+r.length);
11435 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11437 var e = new Roo.data.Record({});
11439 e.set(this.parent.displayField, this.parent.emptyTitle);
11440 e.set(this.parent.valueField, '');
11445 this.fireEvent("load", this, r, options, o);
11446 if(options.callback){
11447 options.callback.call(options.scope || this, r, options, true);
11453 * Loads data from a passed data block. A Reader which understands the format of the data
11454 * must have been configured in the constructor.
11455 * @param {Object} data The data block from which to read the Records. The format of the data expected
11456 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11457 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11459 loadData : function(o, append){
11460 var r = this.reader.readRecords(o);
11461 this.loadRecords(r, {add: append}, true);
11465 * Gets the number of cached records.
11467 * <em>If using paging, this may not be the total size of the dataset. If the data object
11468 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11469 * the data set size</em>
11471 getCount : function(){
11472 return this.data.length || 0;
11476 * Gets the total number of records in the dataset as returned by the server.
11478 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11479 * the dataset size</em>
11481 getTotalCount : function(){
11482 return this.totalLength || 0;
11486 * Returns the sort state of the Store as an object with two properties:
11488 field {String} The name of the field by which the Records are sorted
11489 direction {String} The sort order, "ASC" or "DESC"
11492 getSortState : function(){
11493 return this.sortInfo;
11497 applySort : function(){
11498 if(this.sortInfo && !this.remoteSort){
11499 var s = this.sortInfo, f = s.field;
11500 var st = this.fields.get(f).sortType;
11501 var fn = function(r1, r2){
11502 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11503 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11505 this.data.sort(s.direction, fn);
11506 if(this.snapshot && this.snapshot != this.data){
11507 this.snapshot.sort(s.direction, fn);
11513 * Sets the default sort column and order to be used by the next load operation.
11514 * @param {String} fieldName The name of the field to sort by.
11515 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11517 setDefaultSort : function(field, dir){
11518 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11522 * Sort the Records.
11523 * If remote sorting is used, the sort is performed on the server, and the cache is
11524 * reloaded. If local sorting is used, the cache is sorted internally.
11525 * @param {String} fieldName The name of the field to sort by.
11526 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11528 sort : function(fieldName, dir){
11529 var f = this.fields.get(fieldName);
11531 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11533 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11534 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11539 this.sortToggle[f.name] = dir;
11540 this.sortInfo = {field: f.name, direction: dir};
11541 if(!this.remoteSort){
11543 this.fireEvent("datachanged", this);
11545 this.load(this.lastOptions);
11550 * Calls the specified function for each of the Records in the cache.
11551 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11552 * Returning <em>false</em> aborts and exits the iteration.
11553 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11555 each : function(fn, scope){
11556 this.data.each(fn, scope);
11560 * Gets all records modified since the last commit. Modified records are persisted across load operations
11561 * (e.g., during paging).
11562 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11564 getModifiedRecords : function(){
11565 return this.modified;
11569 createFilterFn : function(property, value, anyMatch){
11570 if(!value.exec){ // not a regex
11571 value = String(value);
11572 if(value.length == 0){
11575 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11577 return function(r){
11578 return value.test(r.data[property]);
11583 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11584 * @param {String} property A field on your records
11585 * @param {Number} start The record index to start at (defaults to 0)
11586 * @param {Number} end The last record index to include (defaults to length - 1)
11587 * @return {Number} The sum
11589 sum : function(property, start, end){
11590 var rs = this.data.items, v = 0;
11591 start = start || 0;
11592 end = (end || end === 0) ? end : rs.length-1;
11594 for(var i = start; i <= end; i++){
11595 v += (rs[i].data[property] || 0);
11601 * Filter the records by a specified property.
11602 * @param {String} field A field on your records
11603 * @param {String/RegExp} value Either a string that the field
11604 * should start with or a RegExp to test against the field
11605 * @param {Boolean} anyMatch True to match any part not just the beginning
11607 filter : function(property, value, anyMatch){
11608 var fn = this.createFilterFn(property, value, anyMatch);
11609 return fn ? this.filterBy(fn) : this.clearFilter();
11613 * Filter by a function. The specified function will be called with each
11614 * record in this data source. If the function returns true the record is included,
11615 * otherwise it is filtered.
11616 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11617 * @param {Object} scope (optional) The scope of the function (defaults to this)
11619 filterBy : function(fn, scope){
11620 this.snapshot = this.snapshot || this.data;
11621 this.data = this.queryBy(fn, scope||this);
11622 this.fireEvent("datachanged", this);
11626 * Query the records by a specified property.
11627 * @param {String} field A field on your records
11628 * @param {String/RegExp} value Either a string that the field
11629 * should start with or a RegExp to test against the field
11630 * @param {Boolean} anyMatch True to match any part not just the beginning
11631 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11633 query : function(property, value, anyMatch){
11634 var fn = this.createFilterFn(property, value, anyMatch);
11635 return fn ? this.queryBy(fn) : this.data.clone();
11639 * Query by a function. The specified function will be called with each
11640 * record in this data source. If the function returns true the record is included
11642 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11643 * @param {Object} scope (optional) The scope of the function (defaults to this)
11644 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11646 queryBy : function(fn, scope){
11647 var data = this.snapshot || this.data;
11648 return data.filterBy(fn, scope||this);
11652 * Collects unique values for a particular dataIndex from this store.
11653 * @param {String} dataIndex The property to collect
11654 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11655 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11656 * @return {Array} An array of the unique values
11658 collect : function(dataIndex, allowNull, bypassFilter){
11659 var d = (bypassFilter === true && this.snapshot) ?
11660 this.snapshot.items : this.data.items;
11661 var v, sv, r = [], l = {};
11662 for(var i = 0, len = d.length; i < len; i++){
11663 v = d[i].data[dataIndex];
11665 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11674 * Revert to a view of the Record cache with no filtering applied.
11675 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11677 clearFilter : function(suppressEvent){
11678 if(this.snapshot && this.snapshot != this.data){
11679 this.data = this.snapshot;
11680 delete this.snapshot;
11681 if(suppressEvent !== true){
11682 this.fireEvent("datachanged", this);
11688 afterEdit : function(record){
11689 if(this.modified.indexOf(record) == -1){
11690 this.modified.push(record);
11692 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11696 afterReject : function(record){
11697 this.modified.remove(record);
11698 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11702 afterCommit : function(record){
11703 this.modified.remove(record);
11704 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11708 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11709 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11711 commitChanges : function(){
11712 var m = this.modified.slice(0);
11713 this.modified = [];
11714 for(var i = 0, len = m.length; i < len; i++){
11720 * Cancel outstanding changes on all changed records.
11722 rejectChanges : function(){
11723 var m = this.modified.slice(0);
11724 this.modified = [];
11725 for(var i = 0, len = m.length; i < len; i++){
11730 onMetaChange : function(meta, rtype, o){
11731 this.recordType = rtype;
11732 this.fields = rtype.prototype.fields;
11733 delete this.snapshot;
11734 this.sortInfo = meta.sortInfo || this.sortInfo;
11735 this.modified = [];
11736 this.fireEvent('metachange', this, this.reader.meta);
11739 moveIndex : function(data, type)
11741 var index = this.indexOf(data);
11743 var newIndex = index + type;
11747 this.insert(newIndex, data);
11752 * Ext JS Library 1.1.1
11753 * Copyright(c) 2006-2007, Ext JS, LLC.
11755 * Originally Released Under LGPL - original licence link has changed is not relivant.
11758 * <script type="text/javascript">
11762 * @class Roo.data.SimpleStore
11763 * @extends Roo.data.Store
11764 * Small helper class to make creating Stores from Array data easier.
11765 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11766 * @cfg {Array} fields An array of field definition objects, or field name strings.
11767 * @cfg {Array} data The multi-dimensional array of data
11769 * @param {Object} config
11771 Roo.data.SimpleStore = function(config){
11772 Roo.data.SimpleStore.superclass.constructor.call(this, {
11774 reader: new Roo.data.ArrayReader({
11777 Roo.data.Record.create(config.fields)
11779 proxy : new Roo.data.MemoryProxy(config.data)
11783 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11785 * Ext JS Library 1.1.1
11786 * Copyright(c) 2006-2007, Ext JS, LLC.
11788 * Originally Released Under LGPL - original licence link has changed is not relivant.
11791 * <script type="text/javascript">
11796 * @extends Roo.data.Store
11797 * @class Roo.data.JsonStore
11798 * Small helper class to make creating Stores for JSON data easier. <br/>
11800 var store = new Roo.data.JsonStore({
11801 url: 'get-images.php',
11803 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11806 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11807 * JsonReader and HttpProxy (unless inline data is provided).</b>
11808 * @cfg {Array} fields An array of field definition objects, or field name strings.
11810 * @param {Object} config
11812 Roo.data.JsonStore = function(c){
11813 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11814 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11815 reader: new Roo.data.JsonReader(c, c.fields)
11818 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11820 * Ext JS Library 1.1.1
11821 * Copyright(c) 2006-2007, Ext JS, LLC.
11823 * Originally Released Under LGPL - original licence link has changed is not relivant.
11826 * <script type="text/javascript">
11830 Roo.data.Field = function(config){
11831 if(typeof config == "string"){
11832 config = {name: config};
11834 Roo.apply(this, config);
11837 this.type = "auto";
11840 var st = Roo.data.SortTypes;
11841 // named sortTypes are supported, here we look them up
11842 if(typeof this.sortType == "string"){
11843 this.sortType = st[this.sortType];
11846 // set default sortType for strings and dates
11847 if(!this.sortType){
11850 this.sortType = st.asUCString;
11853 this.sortType = st.asDate;
11856 this.sortType = st.none;
11861 var stripRe = /[\$,%]/g;
11863 // prebuilt conversion function for this field, instead of
11864 // switching every time we're reading a value
11866 var cv, dateFormat = this.dateFormat;
11871 cv = function(v){ return v; };
11874 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11878 return v !== undefined && v !== null && v !== '' ?
11879 parseInt(String(v).replace(stripRe, ""), 10) : '';
11884 return v !== undefined && v !== null && v !== '' ?
11885 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11890 cv = function(v){ return v === true || v === "true" || v == 1; };
11897 if(v instanceof Date){
11901 if(dateFormat == "timestamp"){
11902 return new Date(v*1000);
11904 return Date.parseDate(v, dateFormat);
11906 var parsed = Date.parse(v);
11907 return parsed ? new Date(parsed) : null;
11916 Roo.data.Field.prototype = {
11924 * Ext JS Library 1.1.1
11925 * Copyright(c) 2006-2007, Ext JS, LLC.
11927 * Originally Released Under LGPL - original licence link has changed is not relivant.
11930 * <script type="text/javascript">
11933 // Base class for reading structured data from a data source. This class is intended to be
11934 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11937 * @class Roo.data.DataReader
11938 * Base class for reading structured data from a data source. This class is intended to be
11939 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11942 Roo.data.DataReader = function(meta, recordType){
11946 this.recordType = recordType instanceof Array ?
11947 Roo.data.Record.create(recordType) : recordType;
11950 Roo.data.DataReader.prototype = {
11952 * Create an empty record
11953 * @param {Object} data (optional) - overlay some values
11954 * @return {Roo.data.Record} record created.
11956 newRow : function(d) {
11958 this.recordType.prototype.fields.each(function(c) {
11960 case 'int' : da[c.name] = 0; break;
11961 case 'date' : da[c.name] = new Date(); break;
11962 case 'float' : da[c.name] = 0.0; break;
11963 case 'boolean' : da[c.name] = false; break;
11964 default : da[c.name] = ""; break;
11968 return new this.recordType(Roo.apply(da, d));
11973 * Ext JS Library 1.1.1
11974 * Copyright(c) 2006-2007, Ext JS, LLC.
11976 * Originally Released Under LGPL - original licence link has changed is not relivant.
11979 * <script type="text/javascript">
11983 * @class Roo.data.DataProxy
11984 * @extends Roo.data.Observable
11985 * This class is an abstract base class for implementations which provide retrieval of
11986 * unformatted data objects.<br>
11988 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11989 * (of the appropriate type which knows how to parse the data object) to provide a block of
11990 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11992 * Custom implementations must implement the load method as described in
11993 * {@link Roo.data.HttpProxy#load}.
11995 Roo.data.DataProxy = function(){
11998 * @event beforeload
11999 * Fires before a network request is made to retrieve a data object.
12000 * @param {Object} This DataProxy object.
12001 * @param {Object} params The params parameter to the load function.
12006 * Fires before the load method's callback is called.
12007 * @param {Object} This DataProxy object.
12008 * @param {Object} o The data object.
12009 * @param {Object} arg The callback argument object passed to the load function.
12013 * @event loadexception
12014 * Fires if an Exception occurs during data retrieval.
12015 * @param {Object} This DataProxy object.
12016 * @param {Object} o The data object.
12017 * @param {Object} arg The callback argument object passed to the load function.
12018 * @param {Object} e The Exception.
12020 loadexception : true
12022 Roo.data.DataProxy.superclass.constructor.call(this);
12025 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12028 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12032 * Ext JS Library 1.1.1
12033 * Copyright(c) 2006-2007, Ext JS, LLC.
12035 * Originally Released Under LGPL - original licence link has changed is not relivant.
12038 * <script type="text/javascript">
12041 * @class Roo.data.MemoryProxy
12042 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12043 * to the Reader when its load method is called.
12045 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12047 Roo.data.MemoryProxy = function(data){
12051 Roo.data.MemoryProxy.superclass.constructor.call(this);
12055 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12058 * Load data from the requested source (in this case an in-memory
12059 * data object passed to the constructor), read the data object into
12060 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12061 * process that block using the passed callback.
12062 * @param {Object} params This parameter is not used by the MemoryProxy class.
12063 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12064 * object into a block of Roo.data.Records.
12065 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12066 * The function must be passed <ul>
12067 * <li>The Record block object</li>
12068 * <li>The "arg" argument from the load function</li>
12069 * <li>A boolean success indicator</li>
12071 * @param {Object} scope The scope in which to call the callback
12072 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12074 load : function(params, reader, callback, scope, arg){
12075 params = params || {};
12078 result = reader.readRecords(this.data);
12080 this.fireEvent("loadexception", this, arg, null, e);
12081 callback.call(scope, null, arg, false);
12084 callback.call(scope, result, arg, true);
12088 update : function(params, records){
12093 * Ext JS Library 1.1.1
12094 * Copyright(c) 2006-2007, Ext JS, LLC.
12096 * Originally Released Under LGPL - original licence link has changed is not relivant.
12099 * <script type="text/javascript">
12102 * @class Roo.data.HttpProxy
12103 * @extends Roo.data.DataProxy
12104 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12105 * configured to reference a certain URL.<br><br>
12107 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12108 * from which the running page was served.<br><br>
12110 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12112 * Be aware that to enable the browser to parse an XML document, the server must set
12113 * the Content-Type header in the HTTP response to "text/xml".
12115 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12116 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12117 * will be used to make the request.
12119 Roo.data.HttpProxy = function(conn){
12120 Roo.data.HttpProxy.superclass.constructor.call(this);
12121 // is conn a conn config or a real conn?
12123 this.useAjax = !conn || !conn.events;
12127 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12128 // thse are take from connection...
12131 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12134 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12135 * extra parameters to each request made by this object. (defaults to undefined)
12138 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12139 * to each request made by this object. (defaults to undefined)
12142 * @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)
12145 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12148 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12154 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12158 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12159 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12160 * a finer-grained basis than the DataProxy events.
12162 getConnection : function(){
12163 return this.useAjax ? Roo.Ajax : this.conn;
12167 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12168 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12169 * process that block using the passed callback.
12170 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12171 * for the request to the remote server.
12172 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12173 * object into a block of Roo.data.Records.
12174 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12175 * The function must be passed <ul>
12176 * <li>The Record block object</li>
12177 * <li>The "arg" argument from the load function</li>
12178 * <li>A boolean success indicator</li>
12180 * @param {Object} scope The scope in which to call the callback
12181 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12183 load : function(params, reader, callback, scope, arg){
12184 if(this.fireEvent("beforeload", this, params) !== false){
12186 params : params || {},
12188 callback : callback,
12193 callback : this.loadResponse,
12197 Roo.applyIf(o, this.conn);
12198 if(this.activeRequest){
12199 Roo.Ajax.abort(this.activeRequest);
12201 this.activeRequest = Roo.Ajax.request(o);
12203 this.conn.request(o);
12206 callback.call(scope||this, null, arg, false);
12211 loadResponse : function(o, success, response){
12212 delete this.activeRequest;
12214 this.fireEvent("loadexception", this, o, response);
12215 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12220 result = o.reader.read(response);
12222 this.fireEvent("loadexception", this, o, response, e);
12223 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12227 this.fireEvent("load", this, o, o.request.arg);
12228 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12232 update : function(dataSet){
12237 updateResponse : function(dataSet){
12242 * Ext JS Library 1.1.1
12243 * Copyright(c) 2006-2007, Ext JS, LLC.
12245 * Originally Released Under LGPL - original licence link has changed is not relivant.
12248 * <script type="text/javascript">
12252 * @class Roo.data.ScriptTagProxy
12253 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12254 * other than the originating domain of the running page.<br><br>
12256 * <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
12257 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12259 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12260 * source code that is used as the source inside a <script> tag.<br><br>
12262 * In order for the browser to process the returned data, the server must wrap the data object
12263 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12264 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12265 * depending on whether the callback name was passed:
12268 boolean scriptTag = false;
12269 String cb = request.getParameter("callback");
12272 response.setContentType("text/javascript");
12274 response.setContentType("application/x-json");
12276 Writer out = response.getWriter();
12278 out.write(cb + "(");
12280 out.print(dataBlock.toJsonString());
12287 * @param {Object} config A configuration object.
12289 Roo.data.ScriptTagProxy = function(config){
12290 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12291 Roo.apply(this, config);
12292 this.head = document.getElementsByTagName("head")[0];
12295 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12297 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12299 * @cfg {String} url The URL from which to request the data object.
12302 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12306 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12307 * the server the name of the callback function set up by the load call to process the returned data object.
12308 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12309 * javascript output which calls this named function passing the data object as its only parameter.
12311 callbackParam : "callback",
12313 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12314 * name to the request.
12319 * Load data from the configured URL, read the data object into
12320 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12321 * process that block using the passed callback.
12322 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12323 * for the request to the remote server.
12324 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12325 * object into a block of Roo.data.Records.
12326 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12327 * The function must be passed <ul>
12328 * <li>The Record block object</li>
12329 * <li>The "arg" argument from the load function</li>
12330 * <li>A boolean success indicator</li>
12332 * @param {Object} scope The scope in which to call the callback
12333 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12335 load : function(params, reader, callback, scope, arg){
12336 if(this.fireEvent("beforeload", this, params) !== false){
12338 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12340 var url = this.url;
12341 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12343 url += "&_dc=" + (new Date().getTime());
12345 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12348 cb : "stcCallback"+transId,
12349 scriptId : "stcScript"+transId,
12353 callback : callback,
12359 window[trans.cb] = function(o){
12360 conn.handleResponse(o, trans);
12363 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12365 if(this.autoAbort !== false){
12369 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12371 var script = document.createElement("script");
12372 script.setAttribute("src", url);
12373 script.setAttribute("type", "text/javascript");
12374 script.setAttribute("id", trans.scriptId);
12375 this.head.appendChild(script);
12377 this.trans = trans;
12379 callback.call(scope||this, null, arg, false);
12384 isLoading : function(){
12385 return this.trans ? true : false;
12389 * Abort the current server request.
12391 abort : function(){
12392 if(this.isLoading()){
12393 this.destroyTrans(this.trans);
12398 destroyTrans : function(trans, isLoaded){
12399 this.head.removeChild(document.getElementById(trans.scriptId));
12400 clearTimeout(trans.timeoutId);
12402 window[trans.cb] = undefined;
12404 delete window[trans.cb];
12407 // if hasn't been loaded, wait for load to remove it to prevent script error
12408 window[trans.cb] = function(){
12409 window[trans.cb] = undefined;
12411 delete window[trans.cb];
12418 handleResponse : function(o, trans){
12419 this.trans = false;
12420 this.destroyTrans(trans, true);
12423 result = trans.reader.readRecords(o);
12425 this.fireEvent("loadexception", this, o, trans.arg, e);
12426 trans.callback.call(trans.scope||window, null, trans.arg, false);
12429 this.fireEvent("load", this, o, trans.arg);
12430 trans.callback.call(trans.scope||window, result, trans.arg, true);
12434 handleFailure : function(trans){
12435 this.trans = false;
12436 this.destroyTrans(trans, false);
12437 this.fireEvent("loadexception", this, null, trans.arg);
12438 trans.callback.call(trans.scope||window, null, trans.arg, false);
12442 * Ext JS Library 1.1.1
12443 * Copyright(c) 2006-2007, Ext JS, LLC.
12445 * Originally Released Under LGPL - original licence link has changed is not relivant.
12448 * <script type="text/javascript">
12452 * @class Roo.data.JsonReader
12453 * @extends Roo.data.DataReader
12454 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12455 * based on mappings in a provided Roo.data.Record constructor.
12457 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12458 * in the reply previously.
12463 var RecordDef = Roo.data.Record.create([
12464 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12465 {name: 'occupation'} // This field will use "occupation" as the mapping.
12467 var myReader = new Roo.data.JsonReader({
12468 totalProperty: "results", // The property which contains the total dataset size (optional)
12469 root: "rows", // The property which contains an Array of row objects
12470 id: "id" // The property within each row object that provides an ID for the record (optional)
12474 * This would consume a JSON file like this:
12476 { 'results': 2, 'rows': [
12477 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12478 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12481 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12482 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12483 * paged from the remote server.
12484 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12485 * @cfg {String} root name of the property which contains the Array of row objects.
12486 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12487 * @cfg {Array} fields Array of field definition objects
12489 * Create a new JsonReader
12490 * @param {Object} meta Metadata configuration options
12491 * @param {Object} recordType Either an Array of field definition objects,
12492 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12494 Roo.data.JsonReader = function(meta, recordType){
12497 // set some defaults:
12498 Roo.applyIf(meta, {
12499 totalProperty: 'total',
12500 successProperty : 'success',
12505 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12507 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12510 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12511 * Used by Store query builder to append _requestMeta to params.
12514 metaFromRemote : false,
12516 * This method is only used by a DataProxy which has retrieved data from a remote server.
12517 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12518 * @return {Object} data A data block which is used by an Roo.data.Store object as
12519 * a cache of Roo.data.Records.
12521 read : function(response){
12522 var json = response.responseText;
12524 var o = /* eval:var:o */ eval("("+json+")");
12526 throw {message: "JsonReader.read: Json object not found"};
12532 this.metaFromRemote = true;
12533 this.meta = o.metaData;
12534 this.recordType = Roo.data.Record.create(o.metaData.fields);
12535 this.onMetaChange(this.meta, this.recordType, o);
12537 return this.readRecords(o);
12540 // private function a store will implement
12541 onMetaChange : function(meta, recordType, o){
12548 simpleAccess: function(obj, subsc) {
12555 getJsonAccessor: function(){
12557 return function(expr) {
12559 return(re.test(expr))
12560 ? new Function("obj", "return obj." + expr)
12565 return Roo.emptyFn;
12570 * Create a data block containing Roo.data.Records from an XML document.
12571 * @param {Object} o An object which contains an Array of row objects in the property specified
12572 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12573 * which contains the total size of the dataset.
12574 * @return {Object} data A data block which is used by an Roo.data.Store object as
12575 * a cache of Roo.data.Records.
12577 readRecords : function(o){
12579 * After any data loads, the raw JSON data is available for further custom processing.
12583 var s = this.meta, Record = this.recordType,
12584 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12586 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12588 if(s.totalProperty) {
12589 this.getTotal = this.getJsonAccessor(s.totalProperty);
12591 if(s.successProperty) {
12592 this.getSuccess = this.getJsonAccessor(s.successProperty);
12594 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12596 var g = this.getJsonAccessor(s.id);
12597 this.getId = function(rec) {
12599 return (r === undefined || r === "") ? null : r;
12602 this.getId = function(){return null;};
12605 for(var jj = 0; jj < fl; jj++){
12607 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12608 this.ef[jj] = this.getJsonAccessor(map);
12612 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12613 if(s.totalProperty){
12614 var vt = parseInt(this.getTotal(o), 10);
12619 if(s.successProperty){
12620 var vs = this.getSuccess(o);
12621 if(vs === false || vs === 'false'){
12626 for(var i = 0; i < c; i++){
12629 var id = this.getId(n);
12630 for(var j = 0; j < fl; j++){
12632 var v = this.ef[j](n);
12634 Roo.log('missing convert for ' + f.name);
12638 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12640 var record = new Record(values, id);
12642 records[i] = record;
12648 totalRecords : totalRecords
12653 * Ext JS Library 1.1.1
12654 * Copyright(c) 2006-2007, Ext JS, LLC.
12656 * Originally Released Under LGPL - original licence link has changed is not relivant.
12659 * <script type="text/javascript">
12663 * @class Roo.data.ArrayReader
12664 * @extends Roo.data.DataReader
12665 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12666 * Each element of that Array represents a row of data fields. The
12667 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12668 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12672 var RecordDef = Roo.data.Record.create([
12673 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12674 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12676 var myReader = new Roo.data.ArrayReader({
12677 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12681 * This would consume an Array like this:
12683 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12685 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12687 * Create a new JsonReader
12688 * @param {Object} meta Metadata configuration options.
12689 * @param {Object} recordType Either an Array of field definition objects
12690 * as specified to {@link Roo.data.Record#create},
12691 * or an {@link Roo.data.Record} object
12692 * created using {@link Roo.data.Record#create}.
12694 Roo.data.ArrayReader = function(meta, recordType){
12695 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12698 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12700 * Create a data block containing Roo.data.Records from an XML document.
12701 * @param {Object} o An Array of row objects which represents the dataset.
12702 * @return {Object} data A data block which is used by an Roo.data.Store object as
12703 * a cache of Roo.data.Records.
12705 readRecords : function(o){
12706 var sid = this.meta ? this.meta.id : null;
12707 var recordType = this.recordType, fields = recordType.prototype.fields;
12710 for(var i = 0; i < root.length; i++){
12713 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12714 for(var j = 0, jlen = fields.length; j < jlen; j++){
12715 var f = fields.items[j];
12716 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12717 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12719 values[f.name] = v;
12721 var record = new recordType(values, id);
12723 records[records.length] = record;
12727 totalRecords : records.length
12736 * @class Roo.bootstrap.ComboBox
12737 * @extends Roo.bootstrap.TriggerField
12738 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12739 * @cfg {Boolean} append (true|false) default false
12740 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12741 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12742 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12743 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12744 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12745 * @cfg {Boolean} animate default true
12746 * @cfg {Boolean} emptyResultText only for touch device
12747 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12748 * @cfg {String} emptyTitle default ''
12750 * Create a new ComboBox.
12751 * @param {Object} config Configuration options
12753 Roo.bootstrap.ComboBox = function(config){
12754 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12758 * Fires when the dropdown list is expanded
12759 * @param {Roo.bootstrap.ComboBox} combo This combo box
12764 * Fires when the dropdown list is collapsed
12765 * @param {Roo.bootstrap.ComboBox} combo This combo box
12769 * @event beforeselect
12770 * Fires before a list item is selected. Return false to cancel the selection.
12771 * @param {Roo.bootstrap.ComboBox} combo This combo box
12772 * @param {Roo.data.Record} record The data record returned from the underlying store
12773 * @param {Number} index The index of the selected item in the dropdown list
12775 'beforeselect' : true,
12778 * Fires when a list item is selected
12779 * @param {Roo.bootstrap.ComboBox} combo This combo box
12780 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12781 * @param {Number} index The index of the selected item in the dropdown list
12785 * @event beforequery
12786 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12787 * The event object passed has these properties:
12788 * @param {Roo.bootstrap.ComboBox} combo This combo box
12789 * @param {String} query The query
12790 * @param {Boolean} forceAll true to force "all" query
12791 * @param {Boolean} cancel true to cancel the query
12792 * @param {Object} e The query event object
12794 'beforequery': true,
12797 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12798 * @param {Roo.bootstrap.ComboBox} combo This combo box
12803 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12804 * @param {Roo.bootstrap.ComboBox} combo This combo box
12805 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12810 * Fires when the remove value from the combobox array
12811 * @param {Roo.bootstrap.ComboBox} combo This combo box
12815 * @event afterremove
12816 * Fires when the remove value from the combobox array
12817 * @param {Roo.bootstrap.ComboBox} combo This combo box
12819 'afterremove' : true,
12821 * @event specialfilter
12822 * Fires when specialfilter
12823 * @param {Roo.bootstrap.ComboBox} combo This combo box
12825 'specialfilter' : true,
12828 * Fires when tick the element
12829 * @param {Roo.bootstrap.ComboBox} combo This combo box
12833 * @event touchviewdisplay
12834 * Fires when touch view require special display (default is using displayField)
12835 * @param {Roo.bootstrap.ComboBox} combo This combo box
12836 * @param {Object} cfg set html .
12838 'touchviewdisplay' : true
12843 this.tickItems = [];
12845 this.selectedIndex = -1;
12846 if(this.mode == 'local'){
12847 if(config.queryDelay === undefined){
12848 this.queryDelay = 10;
12850 if(config.minChars === undefined){
12856 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12859 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12860 * rendering into an Roo.Editor, defaults to false)
12863 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12864 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12867 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12870 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12871 * the dropdown list (defaults to undefined, with no header element)
12875 * @cfg {String/Roo.Template} tpl The template to use to render the output
12879 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12881 listWidth: undefined,
12883 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12884 * mode = 'remote' or 'text' if mode = 'local')
12886 displayField: undefined,
12889 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12890 * mode = 'remote' or 'value' if mode = 'local').
12891 * Note: use of a valueField requires the user make a selection
12892 * in order for a value to be mapped.
12894 valueField: undefined,
12896 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12901 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12902 * field's data value (defaults to the underlying DOM element's name)
12904 hiddenName: undefined,
12906 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12910 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12912 selectedClass: 'active',
12915 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12919 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12920 * anchor positions (defaults to 'tl-bl')
12922 listAlign: 'tl-bl?',
12924 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12928 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12929 * query specified by the allQuery config option (defaults to 'query')
12931 triggerAction: 'query',
12933 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12934 * (defaults to 4, does not apply if editable = false)
12938 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12939 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12943 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12944 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12948 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12949 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12953 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12954 * when editable = true (defaults to false)
12956 selectOnFocus:false,
12958 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12960 queryParam: 'query',
12962 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12963 * when mode = 'remote' (defaults to 'Loading...')
12965 loadingText: 'Loading...',
12967 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12971 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12975 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12976 * traditional select (defaults to true)
12980 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12984 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12988 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12989 * listWidth has a higher value)
12993 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12994 * allow the user to set arbitrary text into the field (defaults to false)
12996 forceSelection:false,
12998 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12999 * if typeAhead = true (defaults to 250)
13001 typeAheadDelay : 250,
13003 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13004 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13006 valueNotFoundText : undefined,
13008 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13010 blockFocus : false,
13013 * @cfg {Boolean} disableClear Disable showing of clear button.
13015 disableClear : false,
13017 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13019 alwaysQuery : false,
13022 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13027 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13029 invalidClass : "has-warning",
13032 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13034 validClass : "has-success",
13037 * @cfg {Boolean} specialFilter (true|false) special filter default false
13039 specialFilter : false,
13042 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13044 mobileTouchView : true,
13047 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13049 useNativeIOS : false,
13052 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13054 mobile_restrict_height : false,
13056 ios_options : false,
13068 btnPosition : 'right',
13069 triggerList : true,
13070 showToggleBtn : true,
13072 emptyResultText: 'Empty',
13073 triggerText : 'Select',
13076 // element that contains real text value.. (when hidden is used..)
13078 getAutoCreate : function()
13083 * Render classic select for iso
13086 if(Roo.isIOS && this.useNativeIOS){
13087 cfg = this.getAutoCreateNativeIOS();
13095 if(Roo.isTouch && this.mobileTouchView){
13096 cfg = this.getAutoCreateTouchView();
13103 if(!this.tickable){
13104 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13109 * ComboBox with tickable selections
13112 var align = this.labelAlign || this.parentLabelAlign();
13115 cls : 'form-group roo-combobox-tickable' //input-group
13118 var btn_text_select = '';
13119 var btn_text_done = '';
13120 var btn_text_cancel = '';
13122 if (this.btn_text_show) {
13123 btn_text_select = 'Select';
13124 btn_text_done = 'Done';
13125 btn_text_cancel = 'Cancel';
13130 cls : 'tickable-buttons',
13135 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13136 //html : this.triggerText
13137 html: btn_text_select
13143 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13145 html: btn_text_done
13151 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13153 html: btn_text_cancel
13159 buttons.cn.unshift({
13161 cls: 'roo-select2-search-field-input'
13167 Roo.each(buttons.cn, function(c){
13169 c.cls += ' btn-' + _this.size;
13172 if (_this.disabled) {
13183 cls: 'form-hidden-field'
13187 cls: 'roo-select2-choices',
13191 cls: 'roo-select2-search-field',
13202 cls: 'roo-select2-container input-group roo-select2-container-multi',
13207 // cls: 'typeahead typeahead-long dropdown-menu',
13208 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13213 if(this.hasFeedback && !this.allowBlank){
13217 cls: 'glyphicon form-control-feedback'
13220 combobox.cn.push(feedback);
13224 if (align ==='left' && this.fieldLabel.length) {
13226 cfg.cls += ' roo-form-group-label-left';
13231 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13232 tooltip : 'This field is required'
13237 cls : 'control-label',
13238 html : this.fieldLabel
13250 var labelCfg = cfg.cn[1];
13251 var contentCfg = cfg.cn[2];
13254 if(this.indicatorpos == 'right'){
13260 cls : 'control-label',
13264 html : this.fieldLabel
13268 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13269 tooltip : 'This field is required'
13284 labelCfg = cfg.cn[0];
13285 contentCfg = cfg.cn[1];
13289 if(this.labelWidth > 12){
13290 labelCfg.style = "width: " + this.labelWidth + 'px';
13293 if(this.labelWidth < 13 && this.labelmd == 0){
13294 this.labelmd = this.labelWidth;
13297 if(this.labellg > 0){
13298 labelCfg.cls += ' col-lg-' + this.labellg;
13299 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13302 if(this.labelmd > 0){
13303 labelCfg.cls += ' col-md-' + this.labelmd;
13304 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13307 if(this.labelsm > 0){
13308 labelCfg.cls += ' col-sm-' + this.labelsm;
13309 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13312 if(this.labelxs > 0){
13313 labelCfg.cls += ' col-xs-' + this.labelxs;
13314 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13318 } else if ( this.fieldLabel.length) {
13319 // Roo.log(" label");
13323 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13324 tooltip : 'This field is required'
13328 //cls : 'input-group-addon',
13329 html : this.fieldLabel
13334 if(this.indicatorpos == 'right'){
13338 //cls : 'input-group-addon',
13339 html : this.fieldLabel
13343 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13344 tooltip : 'This field is required'
13353 // Roo.log(" no label && no align");
13360 ['xs','sm','md','lg'].map(function(size){
13361 if (settings[size]) {
13362 cfg.cls += ' col-' + size + '-' + settings[size];
13370 _initEventsCalled : false,
13373 initEvents: function()
13375 if (this._initEventsCalled) { // as we call render... prevent looping...
13378 this._initEventsCalled = true;
13381 throw "can not find store for combo";
13384 this.indicator = this.indicatorEl();
13386 this.store = Roo.factory(this.store, Roo.data);
13387 this.store.parent = this;
13389 // if we are building from html. then this element is so complex, that we can not really
13390 // use the rendered HTML.
13391 // so we have to trash and replace the previous code.
13392 if (Roo.XComponent.build_from_html) {
13393 // remove this element....
13394 var e = this.el.dom, k=0;
13395 while (e ) { e = e.previousSibling; ++k;}
13400 this.rendered = false;
13402 this.render(this.parent().getChildContainer(true), k);
13405 if(Roo.isIOS && this.useNativeIOS){
13406 this.initIOSView();
13414 if(Roo.isTouch && this.mobileTouchView){
13415 this.initTouchView();
13420 this.initTickableEvents();
13424 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13426 if(this.hiddenName){
13428 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13430 this.hiddenField.dom.value =
13431 this.hiddenValue !== undefined ? this.hiddenValue :
13432 this.value !== undefined ? this.value : '';
13434 // prevent input submission
13435 this.el.dom.removeAttribute('name');
13436 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13441 // this.el.dom.setAttribute('autocomplete', 'off');
13444 var cls = 'x-combo-list';
13446 //this.list = new Roo.Layer({
13447 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13453 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13454 _this.list.setWidth(lw);
13457 this.list.on('mouseover', this.onViewOver, this);
13458 this.list.on('mousemove', this.onViewMove, this);
13459 this.list.on('scroll', this.onViewScroll, this);
13462 this.list.swallowEvent('mousewheel');
13463 this.assetHeight = 0;
13466 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13467 this.assetHeight += this.header.getHeight();
13470 this.innerList = this.list.createChild({cls:cls+'-inner'});
13471 this.innerList.on('mouseover', this.onViewOver, this);
13472 this.innerList.on('mousemove', this.onViewMove, this);
13473 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13475 if(this.allowBlank && !this.pageSize && !this.disableClear){
13476 this.footer = this.list.createChild({cls:cls+'-ft'});
13477 this.pageTb = new Roo.Toolbar(this.footer);
13481 this.footer = this.list.createChild({cls:cls+'-ft'});
13482 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13483 {pageSize: this.pageSize});
13487 if (this.pageTb && this.allowBlank && !this.disableClear) {
13489 this.pageTb.add(new Roo.Toolbar.Fill(), {
13490 cls: 'x-btn-icon x-btn-clear',
13492 handler: function()
13495 _this.clearValue();
13496 _this.onSelect(false, -1);
13501 this.assetHeight += this.footer.getHeight();
13506 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13509 this.view = new Roo.View(this.list, this.tpl, {
13510 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13512 //this.view.wrapEl.setDisplayed(false);
13513 this.view.on('click', this.onViewClick, this);
13516 this.store.on('beforeload', this.onBeforeLoad, this);
13517 this.store.on('load', this.onLoad, this);
13518 this.store.on('loadexception', this.onLoadException, this);
13520 if(this.resizable){
13521 this.resizer = new Roo.Resizable(this.list, {
13522 pinned:true, handles:'se'
13524 this.resizer.on('resize', function(r, w, h){
13525 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13526 this.listWidth = w;
13527 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13528 this.restrictHeight();
13530 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13533 if(!this.editable){
13534 this.editable = true;
13535 this.setEditable(false);
13540 if (typeof(this.events.add.listeners) != 'undefined') {
13542 this.addicon = this.wrap.createChild(
13543 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13545 this.addicon.on('click', function(e) {
13546 this.fireEvent('add', this);
13549 if (typeof(this.events.edit.listeners) != 'undefined') {
13551 this.editicon = this.wrap.createChild(
13552 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13553 if (this.addicon) {
13554 this.editicon.setStyle('margin-left', '40px');
13556 this.editicon.on('click', function(e) {
13558 // we fire even if inothing is selected..
13559 this.fireEvent('edit', this, this.lastData );
13565 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13566 "up" : function(e){
13567 this.inKeyMode = true;
13571 "down" : function(e){
13572 if(!this.isExpanded()){
13573 this.onTriggerClick();
13575 this.inKeyMode = true;
13580 "enter" : function(e){
13581 // this.onViewClick();
13585 if(this.fireEvent("specialkey", this, e)){
13586 this.onViewClick(false);
13592 "esc" : function(e){
13596 "tab" : function(e){
13599 if(this.fireEvent("specialkey", this, e)){
13600 this.onViewClick(false);
13608 doRelay : function(foo, bar, hname){
13609 if(hname == 'down' || this.scope.isExpanded()){
13610 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13619 this.queryDelay = Math.max(this.queryDelay || 10,
13620 this.mode == 'local' ? 10 : 250);
13623 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13625 if(this.typeAhead){
13626 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13628 if(this.editable !== false){
13629 this.inputEl().on("keyup", this.onKeyUp, this);
13631 if(this.forceSelection){
13632 this.inputEl().on('blur', this.doForce, this);
13636 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13637 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13641 initTickableEvents: function()
13645 if(this.hiddenName){
13647 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13649 this.hiddenField.dom.value =
13650 this.hiddenValue !== undefined ? this.hiddenValue :
13651 this.value !== undefined ? this.value : '';
13653 // prevent input submission
13654 this.el.dom.removeAttribute('name');
13655 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13660 // this.list = this.el.select('ul.dropdown-menu',true).first();
13662 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13663 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13664 if(this.triggerList){
13665 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13668 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13669 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13671 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13672 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13674 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13675 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13677 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13678 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13679 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13682 this.cancelBtn.hide();
13687 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13688 _this.list.setWidth(lw);
13691 this.list.on('mouseover', this.onViewOver, this);
13692 this.list.on('mousemove', this.onViewMove, this);
13694 this.list.on('scroll', this.onViewScroll, this);
13697 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13698 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13701 this.view = new Roo.View(this.list, this.tpl, {
13706 selectedClass: this.selectedClass
13709 //this.view.wrapEl.setDisplayed(false);
13710 this.view.on('click', this.onViewClick, this);
13714 this.store.on('beforeload', this.onBeforeLoad, this);
13715 this.store.on('load', this.onLoad, this);
13716 this.store.on('loadexception', this.onLoadException, this);
13719 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13720 "up" : function(e){
13721 this.inKeyMode = true;
13725 "down" : function(e){
13726 this.inKeyMode = true;
13730 "enter" : function(e){
13731 if(this.fireEvent("specialkey", this, e)){
13732 this.onViewClick(false);
13738 "esc" : function(e){
13739 this.onTickableFooterButtonClick(e, false, false);
13742 "tab" : function(e){
13743 this.fireEvent("specialkey", this, e);
13745 this.onTickableFooterButtonClick(e, false, false);
13752 doRelay : function(e, fn, key){
13753 if(this.scope.isExpanded()){
13754 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13763 this.queryDelay = Math.max(this.queryDelay || 10,
13764 this.mode == 'local' ? 10 : 250);
13767 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13769 if(this.typeAhead){
13770 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13773 if(this.editable !== false){
13774 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13777 this.indicator = this.indicatorEl();
13779 if(this.indicator){
13780 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13781 this.indicator.hide();
13786 onDestroy : function(){
13788 this.view.setStore(null);
13789 this.view.el.removeAllListeners();
13790 this.view.el.remove();
13791 this.view.purgeListeners();
13794 this.list.dom.innerHTML = '';
13798 this.store.un('beforeload', this.onBeforeLoad, this);
13799 this.store.un('load', this.onLoad, this);
13800 this.store.un('loadexception', this.onLoadException, this);
13802 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13806 fireKey : function(e){
13807 if(e.isNavKeyPress() && !this.list.isVisible()){
13808 this.fireEvent("specialkey", this, e);
13813 onResize: function(w, h){
13814 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13816 // if(typeof w != 'number'){
13817 // // we do not handle it!?!?
13820 // var tw = this.trigger.getWidth();
13821 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13822 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13824 // this.inputEl().setWidth( this.adjustWidth('input', x));
13826 // //this.trigger.setStyle('left', x+'px');
13828 // if(this.list && this.listWidth === undefined){
13829 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13830 // this.list.setWidth(lw);
13831 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13839 * Allow or prevent the user from directly editing the field text. If false is passed,
13840 * the user will only be able to select from the items defined in the dropdown list. This method
13841 * is the runtime equivalent of setting the 'editable' config option at config time.
13842 * @param {Boolean} value True to allow the user to directly edit the field text
13844 setEditable : function(value){
13845 if(value == this.editable){
13848 this.editable = value;
13850 this.inputEl().dom.setAttribute('readOnly', true);
13851 this.inputEl().on('mousedown', this.onTriggerClick, this);
13852 this.inputEl().addClass('x-combo-noedit');
13854 this.inputEl().dom.setAttribute('readOnly', false);
13855 this.inputEl().un('mousedown', this.onTriggerClick, this);
13856 this.inputEl().removeClass('x-combo-noedit');
13862 onBeforeLoad : function(combo,opts){
13863 if(!this.hasFocus){
13867 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13869 this.restrictHeight();
13870 this.selectedIndex = -1;
13874 onLoad : function(){
13876 this.hasQuery = false;
13878 if(!this.hasFocus){
13882 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13883 this.loading.hide();
13886 if(this.store.getCount() > 0){
13889 this.restrictHeight();
13890 if(this.lastQuery == this.allQuery){
13891 if(this.editable && !this.tickable){
13892 this.inputEl().dom.select();
13896 !this.selectByValue(this.value, true) &&
13899 !this.store.lastOptions ||
13900 typeof(this.store.lastOptions.add) == 'undefined' ||
13901 this.store.lastOptions.add != true
13904 this.select(0, true);
13907 if(this.autoFocus){
13910 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13911 this.taTask.delay(this.typeAheadDelay);
13915 this.onEmptyResults();
13921 onLoadException : function()
13923 this.hasQuery = false;
13925 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13926 this.loading.hide();
13929 if(this.tickable && this.editable){
13934 // only causes errors at present
13935 //Roo.log(this.store.reader.jsonData);
13936 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13938 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13944 onTypeAhead : function(){
13945 if(this.store.getCount() > 0){
13946 var r = this.store.getAt(0);
13947 var newValue = r.data[this.displayField];
13948 var len = newValue.length;
13949 var selStart = this.getRawValue().length;
13951 if(selStart != len){
13952 this.setRawValue(newValue);
13953 this.selectText(selStart, newValue.length);
13959 onSelect : function(record, index){
13961 if(this.fireEvent('beforeselect', this, record, index) !== false){
13963 this.setFromData(index > -1 ? record.data : false);
13966 this.fireEvent('select', this, record, index);
13971 * Returns the currently selected field value or empty string if no value is set.
13972 * @return {String} value The selected value
13974 getValue : function()
13976 if(Roo.isIOS && this.useNativeIOS){
13977 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13981 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13984 if(this.valueField){
13985 return typeof this.value != 'undefined' ? this.value : '';
13987 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13991 getRawValue : function()
13993 if(Roo.isIOS && this.useNativeIOS){
13994 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13997 var v = this.inputEl().getValue();
14003 * Clears any text/value currently set in the field
14005 clearValue : function(){
14007 if(this.hiddenField){
14008 this.hiddenField.dom.value = '';
14011 this.setRawValue('');
14012 this.lastSelectionText = '';
14013 this.lastData = false;
14015 var close = this.closeTriggerEl();
14026 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14027 * will be displayed in the field. If the value does not match the data value of an existing item,
14028 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14029 * Otherwise the field will be blank (although the value will still be set).
14030 * @param {String} value The value to match
14032 setValue : function(v)
14034 if(Roo.isIOS && this.useNativeIOS){
14035 this.setIOSValue(v);
14045 if(this.valueField){
14046 var r = this.findRecord(this.valueField, v);
14048 text = r.data[this.displayField];
14049 }else if(this.valueNotFoundText !== undefined){
14050 text = this.valueNotFoundText;
14053 this.lastSelectionText = text;
14054 if(this.hiddenField){
14055 this.hiddenField.dom.value = v;
14057 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14060 var close = this.closeTriggerEl();
14063 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14069 * @property {Object} the last set data for the element
14074 * Sets the value of the field based on a object which is related to the record format for the store.
14075 * @param {Object} value the value to set as. or false on reset?
14077 setFromData : function(o){
14084 var dv = ''; // display value
14085 var vv = ''; // value value..
14087 if (this.displayField) {
14088 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14090 // this is an error condition!!!
14091 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14094 if(this.valueField){
14095 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14098 var close = this.closeTriggerEl();
14101 if(dv.length || vv * 1 > 0){
14103 this.blockFocus=true;
14109 if(this.hiddenField){
14110 this.hiddenField.dom.value = vv;
14112 this.lastSelectionText = dv;
14113 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14117 // no hidden field.. - we store the value in 'value', but still display
14118 // display field!!!!
14119 this.lastSelectionText = dv;
14120 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14127 reset : function(){
14128 // overridden so that last data is reset..
14135 this.setValue(this.originalValue);
14136 //this.clearInvalid();
14137 this.lastData = false;
14139 this.view.clearSelections();
14145 findRecord : function(prop, value){
14147 if(this.store.getCount() > 0){
14148 this.store.each(function(r){
14149 if(r.data[prop] == value){
14159 getName: function()
14161 // returns hidden if it's set..
14162 if (!this.rendered) {return ''};
14163 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14167 onViewMove : function(e, t){
14168 this.inKeyMode = false;
14172 onViewOver : function(e, t){
14173 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14176 var item = this.view.findItemFromChild(t);
14179 var index = this.view.indexOf(item);
14180 this.select(index, false);
14185 onViewClick : function(view, doFocus, el, e)
14187 var index = this.view.getSelectedIndexes()[0];
14189 var r = this.store.getAt(index);
14193 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14200 Roo.each(this.tickItems, function(v,k){
14202 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14204 _this.tickItems.splice(k, 1);
14206 if(typeof(e) == 'undefined' && view == false){
14207 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14219 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14220 this.tickItems.push(r.data);
14223 if(typeof(e) == 'undefined' && view == false){
14224 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14231 this.onSelect(r, index);
14233 if(doFocus !== false && !this.blockFocus){
14234 this.inputEl().focus();
14239 restrictHeight : function(){
14240 //this.innerList.dom.style.height = '';
14241 //var inner = this.innerList.dom;
14242 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14243 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14244 //this.list.beginUpdate();
14245 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14246 this.list.alignTo(this.inputEl(), this.listAlign);
14247 this.list.alignTo(this.inputEl(), this.listAlign);
14248 //this.list.endUpdate();
14252 onEmptyResults : function(){
14254 if(this.tickable && this.editable){
14255 this.hasFocus = false;
14256 this.restrictHeight();
14264 * Returns true if the dropdown list is expanded, else false.
14266 isExpanded : function(){
14267 return this.list.isVisible();
14271 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14272 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14273 * @param {String} value The data value of the item to select
14274 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14275 * selected item if it is not currently in view (defaults to true)
14276 * @return {Boolean} True if the value matched an item in the list, else false
14278 selectByValue : function(v, scrollIntoView){
14279 if(v !== undefined && v !== null){
14280 var r = this.findRecord(this.valueField || this.displayField, v);
14282 this.select(this.store.indexOf(r), scrollIntoView);
14290 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14291 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14292 * @param {Number} index The zero-based index of the list item to select
14293 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14294 * selected item if it is not currently in view (defaults to true)
14296 select : function(index, scrollIntoView){
14297 this.selectedIndex = index;
14298 this.view.select(index);
14299 if(scrollIntoView !== false){
14300 var el = this.view.getNode(index);
14302 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14305 this.list.scrollChildIntoView(el, false);
14311 selectNext : function(){
14312 var ct = this.store.getCount();
14314 if(this.selectedIndex == -1){
14316 }else if(this.selectedIndex < ct-1){
14317 this.select(this.selectedIndex+1);
14323 selectPrev : function(){
14324 var ct = this.store.getCount();
14326 if(this.selectedIndex == -1){
14328 }else if(this.selectedIndex != 0){
14329 this.select(this.selectedIndex-1);
14335 onKeyUp : function(e){
14336 if(this.editable !== false && !e.isSpecialKey()){
14337 this.lastKey = e.getKey();
14338 this.dqTask.delay(this.queryDelay);
14343 validateBlur : function(){
14344 return !this.list || !this.list.isVisible();
14348 initQuery : function(){
14350 var v = this.getRawValue();
14352 if(this.tickable && this.editable){
14353 v = this.tickableInputEl().getValue();
14360 doForce : function(){
14361 if(this.inputEl().dom.value.length > 0){
14362 this.inputEl().dom.value =
14363 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14369 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14370 * query allowing the query action to be canceled if needed.
14371 * @param {String} query The SQL query to execute
14372 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14373 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14374 * saved in the current store (defaults to false)
14376 doQuery : function(q, forceAll){
14378 if(q === undefined || q === null){
14383 forceAll: forceAll,
14387 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14392 forceAll = qe.forceAll;
14393 if(forceAll === true || (q.length >= this.minChars)){
14395 this.hasQuery = true;
14397 if(this.lastQuery != q || this.alwaysQuery){
14398 this.lastQuery = q;
14399 if(this.mode == 'local'){
14400 this.selectedIndex = -1;
14402 this.store.clearFilter();
14405 if(this.specialFilter){
14406 this.fireEvent('specialfilter', this);
14411 this.store.filter(this.displayField, q);
14414 this.store.fireEvent("datachanged", this.store);
14421 this.store.baseParams[this.queryParam] = q;
14423 var options = {params : this.getParams(q)};
14426 options.add = true;
14427 options.params.start = this.page * this.pageSize;
14430 this.store.load(options);
14433 * this code will make the page width larger, at the beginning, the list not align correctly,
14434 * we should expand the list on onLoad
14435 * so command out it
14440 this.selectedIndex = -1;
14445 this.loadNext = false;
14449 getParams : function(q){
14451 //p[this.queryParam] = q;
14455 p.limit = this.pageSize;
14461 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14463 collapse : function(){
14464 if(!this.isExpanded()){
14470 this.hasFocus = false;
14474 this.cancelBtn.hide();
14475 this.trigger.show();
14478 this.tickableInputEl().dom.value = '';
14479 this.tickableInputEl().blur();
14484 Roo.get(document).un('mousedown', this.collapseIf, this);
14485 Roo.get(document).un('mousewheel', this.collapseIf, this);
14486 if (!this.editable) {
14487 Roo.get(document).un('keydown', this.listKeyPress, this);
14489 this.fireEvent('collapse', this);
14495 collapseIf : function(e){
14496 var in_combo = e.within(this.el);
14497 var in_list = e.within(this.list);
14498 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14500 if (in_combo || in_list || is_list) {
14501 //e.stopPropagation();
14506 this.onTickableFooterButtonClick(e, false, false);
14514 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14516 expand : function(){
14518 if(this.isExpanded() || !this.hasFocus){
14522 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14523 this.list.setWidth(lw);
14529 this.restrictHeight();
14533 this.tickItems = Roo.apply([], this.item);
14536 this.cancelBtn.show();
14537 this.trigger.hide();
14540 this.tickableInputEl().focus();
14545 Roo.get(document).on('mousedown', this.collapseIf, this);
14546 Roo.get(document).on('mousewheel', this.collapseIf, this);
14547 if (!this.editable) {
14548 Roo.get(document).on('keydown', this.listKeyPress, this);
14551 this.fireEvent('expand', this);
14555 // Implements the default empty TriggerField.onTriggerClick function
14556 onTriggerClick : function(e)
14558 Roo.log('trigger click');
14560 if(this.disabled || !this.triggerList){
14565 this.loadNext = false;
14567 if(this.isExpanded()){
14569 if (!this.blockFocus) {
14570 this.inputEl().focus();
14574 this.hasFocus = true;
14575 if(this.triggerAction == 'all') {
14576 this.doQuery(this.allQuery, true);
14578 this.doQuery(this.getRawValue());
14580 if (!this.blockFocus) {
14581 this.inputEl().focus();
14586 onTickableTriggerClick : function(e)
14593 this.loadNext = false;
14594 this.hasFocus = true;
14596 if(this.triggerAction == 'all') {
14597 this.doQuery(this.allQuery, true);
14599 this.doQuery(this.getRawValue());
14603 onSearchFieldClick : function(e)
14605 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14606 this.onTickableFooterButtonClick(e, false, false);
14610 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14615 this.loadNext = false;
14616 this.hasFocus = true;
14618 if(this.triggerAction == 'all') {
14619 this.doQuery(this.allQuery, true);
14621 this.doQuery(this.getRawValue());
14625 listKeyPress : function(e)
14627 //Roo.log('listkeypress');
14628 // scroll to first matching element based on key pres..
14629 if (e.isSpecialKey()) {
14632 var k = String.fromCharCode(e.getKey()).toUpperCase();
14635 var csel = this.view.getSelectedNodes();
14636 var cselitem = false;
14638 var ix = this.view.indexOf(csel[0]);
14639 cselitem = this.store.getAt(ix);
14640 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14646 this.store.each(function(v) {
14648 // start at existing selection.
14649 if (cselitem.id == v.id) {
14655 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14656 match = this.store.indexOf(v);
14662 if (match === false) {
14663 return true; // no more action?
14666 this.view.select(match);
14667 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14668 sn.scrollIntoView(sn.dom.parentNode, false);
14671 onViewScroll : function(e, t){
14673 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){
14677 this.hasQuery = true;
14679 this.loading = this.list.select('.loading', true).first();
14681 if(this.loading === null){
14682 this.list.createChild({
14684 cls: 'loading roo-select2-more-results roo-select2-active',
14685 html: 'Loading more results...'
14688 this.loading = this.list.select('.loading', true).first();
14690 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14692 this.loading.hide();
14695 this.loading.show();
14700 this.loadNext = true;
14702 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14707 addItem : function(o)
14709 var dv = ''; // display value
14711 if (this.displayField) {
14712 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14714 // this is an error condition!!!
14715 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14722 var choice = this.choices.createChild({
14724 cls: 'roo-select2-search-choice',
14733 cls: 'roo-select2-search-choice-close fa fa-times',
14738 }, this.searchField);
14740 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14742 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14750 this.inputEl().dom.value = '';
14755 onRemoveItem : function(e, _self, o)
14757 e.preventDefault();
14759 this.lastItem = Roo.apply([], this.item);
14761 var index = this.item.indexOf(o.data) * 1;
14764 Roo.log('not this item?!');
14768 this.item.splice(index, 1);
14773 this.fireEvent('remove', this, e);
14779 syncValue : function()
14781 if(!this.item.length){
14788 Roo.each(this.item, function(i){
14789 if(_this.valueField){
14790 value.push(i[_this.valueField]);
14797 this.value = value.join(',');
14799 if(this.hiddenField){
14800 this.hiddenField.dom.value = this.value;
14803 this.store.fireEvent("datachanged", this.store);
14808 clearItem : function()
14810 if(!this.multiple){
14816 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14824 if(this.tickable && !Roo.isTouch){
14825 this.view.refresh();
14829 inputEl: function ()
14831 if(Roo.isIOS && this.useNativeIOS){
14832 return this.el.select('select.roo-ios-select', true).first();
14835 if(Roo.isTouch && this.mobileTouchView){
14836 return this.el.select('input.form-control',true).first();
14840 return this.searchField;
14843 return this.el.select('input.form-control',true).first();
14846 onTickableFooterButtonClick : function(e, btn, el)
14848 e.preventDefault();
14850 this.lastItem = Roo.apply([], this.item);
14852 if(btn && btn.name == 'cancel'){
14853 this.tickItems = Roo.apply([], this.item);
14862 Roo.each(this.tickItems, function(o){
14870 validate : function()
14872 if(this.getVisibilityEl().hasClass('hidden')){
14876 var v = this.getRawValue();
14879 v = this.getValue();
14882 if(this.disabled || this.allowBlank || v.length){
14887 this.markInvalid();
14891 tickableInputEl : function()
14893 if(!this.tickable || !this.editable){
14894 return this.inputEl();
14897 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14901 getAutoCreateTouchView : function()
14906 cls: 'form-group' //input-group
14912 type : this.inputType,
14913 cls : 'form-control x-combo-noedit',
14914 autocomplete: 'new-password',
14915 placeholder : this.placeholder || '',
14920 input.name = this.name;
14924 input.cls += ' input-' + this.size;
14927 if (this.disabled) {
14928 input.disabled = true;
14939 inputblock.cls += ' input-group';
14941 inputblock.cn.unshift({
14943 cls : 'input-group-addon',
14948 if(this.removable && !this.multiple){
14949 inputblock.cls += ' roo-removable';
14951 inputblock.cn.push({
14954 cls : 'roo-combo-removable-btn close'
14958 if(this.hasFeedback && !this.allowBlank){
14960 inputblock.cls += ' has-feedback';
14962 inputblock.cn.push({
14964 cls: 'glyphicon form-control-feedback'
14971 inputblock.cls += (this.before) ? '' : ' input-group';
14973 inputblock.cn.push({
14975 cls : 'input-group-addon',
14986 cls: 'form-hidden-field'
15000 cls: 'form-hidden-field'
15004 cls: 'roo-select2-choices',
15008 cls: 'roo-select2-search-field',
15021 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15027 if(!this.multiple && this.showToggleBtn){
15034 if (this.caret != false) {
15037 cls: 'fa fa-' + this.caret
15044 cls : 'input-group-addon btn dropdown-toggle',
15049 cls: 'combobox-clear',
15063 combobox.cls += ' roo-select2-container-multi';
15066 var align = this.labelAlign || this.parentLabelAlign();
15068 if (align ==='left' && this.fieldLabel.length) {
15073 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15074 tooltip : 'This field is required'
15078 cls : 'control-label',
15079 html : this.fieldLabel
15090 var labelCfg = cfg.cn[1];
15091 var contentCfg = cfg.cn[2];
15094 if(this.indicatorpos == 'right'){
15099 cls : 'control-label',
15103 html : this.fieldLabel
15107 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15108 tooltip : 'This field is required'
15121 labelCfg = cfg.cn[0];
15122 contentCfg = cfg.cn[1];
15127 if(this.labelWidth > 12){
15128 labelCfg.style = "width: " + this.labelWidth + 'px';
15131 if(this.labelWidth < 13 && this.labelmd == 0){
15132 this.labelmd = this.labelWidth;
15135 if(this.labellg > 0){
15136 labelCfg.cls += ' col-lg-' + this.labellg;
15137 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15140 if(this.labelmd > 0){
15141 labelCfg.cls += ' col-md-' + this.labelmd;
15142 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15145 if(this.labelsm > 0){
15146 labelCfg.cls += ' col-sm-' + this.labelsm;
15147 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15150 if(this.labelxs > 0){
15151 labelCfg.cls += ' col-xs-' + this.labelxs;
15152 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15156 } else if ( this.fieldLabel.length) {
15160 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15161 tooltip : 'This field is required'
15165 cls : 'control-label',
15166 html : this.fieldLabel
15177 if(this.indicatorpos == 'right'){
15181 cls : 'control-label',
15182 html : this.fieldLabel,
15186 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15187 tooltip : 'This field is required'
15204 var settings = this;
15206 ['xs','sm','md','lg'].map(function(size){
15207 if (settings[size]) {
15208 cfg.cls += ' col-' + size + '-' + settings[size];
15215 initTouchView : function()
15217 this.renderTouchView();
15219 this.touchViewEl.on('scroll', function(){
15220 this.el.dom.scrollTop = 0;
15223 this.originalValue = this.getValue();
15225 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15227 this.inputEl().on("click", this.showTouchView, this);
15228 if (this.triggerEl) {
15229 this.triggerEl.on("click", this.showTouchView, this);
15233 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15234 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15236 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15238 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15239 this.store.on('load', this.onTouchViewLoad, this);
15240 this.store.on('loadexception', this.onTouchViewLoadException, this);
15242 if(this.hiddenName){
15244 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15246 this.hiddenField.dom.value =
15247 this.hiddenValue !== undefined ? this.hiddenValue :
15248 this.value !== undefined ? this.value : '';
15250 this.el.dom.removeAttribute('name');
15251 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15255 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15256 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15259 if(this.removable && !this.multiple){
15260 var close = this.closeTriggerEl();
15262 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15263 close.on('click', this.removeBtnClick, this, close);
15267 * fix the bug in Safari iOS8
15269 this.inputEl().on("focus", function(e){
15270 document.activeElement.blur();
15273 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15280 renderTouchView : function()
15282 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15283 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15285 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15286 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15288 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15289 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15290 this.touchViewBodyEl.setStyle('overflow', 'auto');
15292 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15293 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15295 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15296 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15300 showTouchView : function()
15306 this.touchViewHeaderEl.hide();
15308 if(this.modalTitle.length){
15309 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15310 this.touchViewHeaderEl.show();
15313 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15314 this.touchViewEl.show();
15316 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15318 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15319 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15321 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15323 if(this.modalTitle.length){
15324 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15327 this.touchViewBodyEl.setHeight(bodyHeight);
15331 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15333 this.touchViewEl.addClass('in');
15336 if(this._touchViewMask){
15337 Roo.get(document.body).addClass("x-body-masked");
15338 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15339 this._touchViewMask.setStyle('z-index', 10000);
15340 this._touchViewMask.addClass('show');
15343 this.doTouchViewQuery();
15347 hideTouchView : function()
15349 this.touchViewEl.removeClass('in');
15353 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15355 this.touchViewEl.setStyle('display', 'none');
15358 if(this._touchViewMask){
15359 this._touchViewMask.removeClass('show');
15360 Roo.get(document.body).removeClass("x-body-masked");
15364 setTouchViewValue : function()
15371 Roo.each(this.tickItems, function(o){
15376 this.hideTouchView();
15379 doTouchViewQuery : function()
15388 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15392 if(!this.alwaysQuery || this.mode == 'local'){
15393 this.onTouchViewLoad();
15400 onTouchViewBeforeLoad : function(combo,opts)
15406 onTouchViewLoad : function()
15408 if(this.store.getCount() < 1){
15409 this.onTouchViewEmptyResults();
15413 this.clearTouchView();
15415 var rawValue = this.getRawValue();
15417 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15419 this.tickItems = [];
15421 this.store.data.each(function(d, rowIndex){
15422 var row = this.touchViewListGroup.createChild(template);
15424 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15425 row.addClass(d.data.cls);
15428 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15431 html : d.data[this.displayField]
15434 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15435 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15438 row.removeClass('selected');
15439 if(!this.multiple && this.valueField &&
15440 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15443 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15444 row.addClass('selected');
15447 if(this.multiple && this.valueField &&
15448 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15452 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15453 this.tickItems.push(d.data);
15456 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15460 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15462 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15464 if(this.modalTitle.length){
15465 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15468 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15470 if(this.mobile_restrict_height && listHeight < bodyHeight){
15471 this.touchViewBodyEl.setHeight(listHeight);
15476 if(firstChecked && listHeight > bodyHeight){
15477 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15482 onTouchViewLoadException : function()
15484 this.hideTouchView();
15487 onTouchViewEmptyResults : function()
15489 this.clearTouchView();
15491 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15493 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15497 clearTouchView : function()
15499 this.touchViewListGroup.dom.innerHTML = '';
15502 onTouchViewClick : function(e, el, o)
15504 e.preventDefault();
15507 var rowIndex = o.rowIndex;
15509 var r = this.store.getAt(rowIndex);
15511 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15513 if(!this.multiple){
15514 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15515 c.dom.removeAttribute('checked');
15518 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15520 this.setFromData(r.data);
15522 var close = this.closeTriggerEl();
15528 this.hideTouchView();
15530 this.fireEvent('select', this, r, rowIndex);
15535 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15536 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15537 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15541 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15542 this.addItem(r.data);
15543 this.tickItems.push(r.data);
15547 getAutoCreateNativeIOS : function()
15550 cls: 'form-group' //input-group,
15555 cls : 'roo-ios-select'
15559 combobox.name = this.name;
15562 if (this.disabled) {
15563 combobox.disabled = true;
15566 var settings = this;
15568 ['xs','sm','md','lg'].map(function(size){
15569 if (settings[size]) {
15570 cfg.cls += ' col-' + size + '-' + settings[size];
15580 initIOSView : function()
15582 this.store.on('load', this.onIOSViewLoad, this);
15587 onIOSViewLoad : function()
15589 if(this.store.getCount() < 1){
15593 this.clearIOSView();
15595 if(this.allowBlank) {
15597 var default_text = '-- SELECT --';
15599 if(this.placeholder.length){
15600 default_text = this.placeholder;
15603 if(this.emptyTitle.length){
15604 default_text += ' - ' + this.emptyTitle + ' -';
15607 var opt = this.inputEl().createChild({
15610 html : default_text
15614 o[this.valueField] = 0;
15615 o[this.displayField] = default_text;
15617 this.ios_options.push({
15624 this.store.data.each(function(d, rowIndex){
15628 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15629 html = d.data[this.displayField];
15634 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15635 value = d.data[this.valueField];
15644 if(this.value == d.data[this.valueField]){
15645 option['selected'] = true;
15648 var opt = this.inputEl().createChild(option);
15650 this.ios_options.push({
15657 this.inputEl().on('change', function(){
15658 this.fireEvent('select', this);
15663 clearIOSView: function()
15665 this.inputEl().dom.innerHTML = '';
15667 this.ios_options = [];
15670 setIOSValue: function(v)
15674 if(!this.ios_options){
15678 Roo.each(this.ios_options, function(opts){
15680 opts.el.dom.removeAttribute('selected');
15682 if(opts.data[this.valueField] != v){
15686 opts.el.dom.setAttribute('selected', true);
15692 * @cfg {Boolean} grow
15696 * @cfg {Number} growMin
15700 * @cfg {Number} growMax
15709 Roo.apply(Roo.bootstrap.ComboBox, {
15713 cls: 'modal-header',
15735 cls: 'list-group-item',
15739 cls: 'roo-combobox-list-group-item-value'
15743 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15757 listItemCheckbox : {
15759 cls: 'list-group-item',
15763 cls: 'roo-combobox-list-group-item-value'
15767 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15783 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15788 cls: 'modal-footer',
15796 cls: 'col-xs-6 text-left',
15799 cls: 'btn btn-danger roo-touch-view-cancel',
15805 cls: 'col-xs-6 text-right',
15808 cls: 'btn btn-success roo-touch-view-ok',
15819 Roo.apply(Roo.bootstrap.ComboBox, {
15821 touchViewTemplate : {
15823 cls: 'modal fade roo-combobox-touch-view',
15827 cls: 'modal-dialog',
15828 style : 'position:fixed', // we have to fix position....
15832 cls: 'modal-content',
15834 Roo.bootstrap.ComboBox.header,
15835 Roo.bootstrap.ComboBox.body,
15836 Roo.bootstrap.ComboBox.footer
15845 * Ext JS Library 1.1.1
15846 * Copyright(c) 2006-2007, Ext JS, LLC.
15848 * Originally Released Under LGPL - original licence link has changed is not relivant.
15851 * <script type="text/javascript">
15856 * @extends Roo.util.Observable
15857 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15858 * This class also supports single and multi selection modes. <br>
15859 * Create a data model bound view:
15861 var store = new Roo.data.Store(...);
15863 var view = new Roo.View({
15865 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15867 singleSelect: true,
15868 selectedClass: "ydataview-selected",
15872 // listen for node click?
15873 view.on("click", function(vw, index, node, e){
15874 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15878 dataModel.load("foobar.xml");
15880 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15882 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15883 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15885 * Note: old style constructor is still suported (container, template, config)
15888 * Create a new View
15889 * @param {Object} config The config object
15892 Roo.View = function(config, depreciated_tpl, depreciated_config){
15894 this.parent = false;
15896 if (typeof(depreciated_tpl) == 'undefined') {
15897 // new way.. - universal constructor.
15898 Roo.apply(this, config);
15899 this.el = Roo.get(this.el);
15902 this.el = Roo.get(config);
15903 this.tpl = depreciated_tpl;
15904 Roo.apply(this, depreciated_config);
15906 this.wrapEl = this.el.wrap().wrap();
15907 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15910 if(typeof(this.tpl) == "string"){
15911 this.tpl = new Roo.Template(this.tpl);
15913 // support xtype ctors..
15914 this.tpl = new Roo.factory(this.tpl, Roo);
15918 this.tpl.compile();
15923 * @event beforeclick
15924 * Fires before a click is processed. Returns false to cancel the default action.
15925 * @param {Roo.View} this
15926 * @param {Number} index The index of the target node
15927 * @param {HTMLElement} node The target node
15928 * @param {Roo.EventObject} e The raw event object
15930 "beforeclick" : true,
15933 * Fires when a template node is clicked.
15934 * @param {Roo.View} this
15935 * @param {Number} index The index of the target node
15936 * @param {HTMLElement} node The target node
15937 * @param {Roo.EventObject} e The raw event object
15942 * Fires when a template node is double clicked.
15943 * @param {Roo.View} this
15944 * @param {Number} index The index of the target node
15945 * @param {HTMLElement} node The target node
15946 * @param {Roo.EventObject} e The raw event object
15950 * @event contextmenu
15951 * Fires when a template node is right clicked.
15952 * @param {Roo.View} this
15953 * @param {Number} index The index of the target node
15954 * @param {HTMLElement} node The target node
15955 * @param {Roo.EventObject} e The raw event object
15957 "contextmenu" : true,
15959 * @event selectionchange
15960 * Fires when the selected nodes change.
15961 * @param {Roo.View} this
15962 * @param {Array} selections Array of the selected nodes
15964 "selectionchange" : true,
15967 * @event beforeselect
15968 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15969 * @param {Roo.View} this
15970 * @param {HTMLElement} node The node to be selected
15971 * @param {Array} selections Array of currently selected nodes
15973 "beforeselect" : true,
15975 * @event preparedata
15976 * Fires on every row to render, to allow you to change the data.
15977 * @param {Roo.View} this
15978 * @param {Object} data to be rendered (change this)
15980 "preparedata" : true
15988 "click": this.onClick,
15989 "dblclick": this.onDblClick,
15990 "contextmenu": this.onContextMenu,
15994 this.selections = [];
15996 this.cmp = new Roo.CompositeElementLite([]);
15998 this.store = Roo.factory(this.store, Roo.data);
15999 this.setStore(this.store, true);
16002 if ( this.footer && this.footer.xtype) {
16004 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16006 this.footer.dataSource = this.store;
16007 this.footer.container = fctr;
16008 this.footer = Roo.factory(this.footer, Roo);
16009 fctr.insertFirst(this.el);
16011 // this is a bit insane - as the paging toolbar seems to detach the el..
16012 // dom.parentNode.parentNode.parentNode
16013 // they get detached?
16017 Roo.View.superclass.constructor.call(this);
16022 Roo.extend(Roo.View, Roo.util.Observable, {
16025 * @cfg {Roo.data.Store} store Data store to load data from.
16030 * @cfg {String|Roo.Element} el The container element.
16035 * @cfg {String|Roo.Template} tpl The template used by this View
16039 * @cfg {String} dataName the named area of the template to use as the data area
16040 * Works with domtemplates roo-name="name"
16044 * @cfg {String} selectedClass The css class to add to selected nodes
16046 selectedClass : "x-view-selected",
16048 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16053 * @cfg {String} text to display on mask (default Loading)
16057 * @cfg {Boolean} multiSelect Allow multiple selection
16059 multiSelect : false,
16061 * @cfg {Boolean} singleSelect Allow single selection
16063 singleSelect: false,
16066 * @cfg {Boolean} toggleSelect - selecting
16068 toggleSelect : false,
16071 * @cfg {Boolean} tickable - selecting
16076 * Returns the element this view is bound to.
16077 * @return {Roo.Element}
16079 getEl : function(){
16080 return this.wrapEl;
16086 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16088 refresh : function(){
16089 //Roo.log('refresh');
16092 // if we are using something like 'domtemplate', then
16093 // the what gets used is:
16094 // t.applySubtemplate(NAME, data, wrapping data..)
16095 // the outer template then get' applied with
16096 // the store 'extra data'
16097 // and the body get's added to the
16098 // roo-name="data" node?
16099 // <span class='roo-tpl-{name}'></span> ?????
16103 this.clearSelections();
16104 this.el.update("");
16106 var records = this.store.getRange();
16107 if(records.length < 1) {
16109 // is this valid?? = should it render a template??
16111 this.el.update(this.emptyText);
16115 if (this.dataName) {
16116 this.el.update(t.apply(this.store.meta)); //????
16117 el = this.el.child('.roo-tpl-' + this.dataName);
16120 for(var i = 0, len = records.length; i < len; i++){
16121 var data = this.prepareData(records[i].data, i, records[i]);
16122 this.fireEvent("preparedata", this, data, i, records[i]);
16124 var d = Roo.apply({}, data);
16127 Roo.apply(d, {'roo-id' : Roo.id()});
16131 Roo.each(this.parent.item, function(item){
16132 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16135 Roo.apply(d, {'roo-data-checked' : 'checked'});
16139 html[html.length] = Roo.util.Format.trim(
16141 t.applySubtemplate(this.dataName, d, this.store.meta) :
16148 el.update(html.join(""));
16149 this.nodes = el.dom.childNodes;
16150 this.updateIndexes(0);
16155 * Function to override to reformat the data that is sent to
16156 * the template for each node.
16157 * DEPRICATED - use the preparedata event handler.
16158 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16159 * a JSON object for an UpdateManager bound view).
16161 prepareData : function(data, index, record)
16163 this.fireEvent("preparedata", this, data, index, record);
16167 onUpdate : function(ds, record){
16168 // Roo.log('on update');
16169 this.clearSelections();
16170 var index = this.store.indexOf(record);
16171 var n = this.nodes[index];
16172 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16173 n.parentNode.removeChild(n);
16174 this.updateIndexes(index, index);
16180 onAdd : function(ds, records, index)
16182 //Roo.log(['on Add', ds, records, index] );
16183 this.clearSelections();
16184 if(this.nodes.length == 0){
16188 var n = this.nodes[index];
16189 for(var i = 0, len = records.length; i < len; i++){
16190 var d = this.prepareData(records[i].data, i, records[i]);
16192 this.tpl.insertBefore(n, d);
16195 this.tpl.append(this.el, d);
16198 this.updateIndexes(index);
16201 onRemove : function(ds, record, index){
16202 // Roo.log('onRemove');
16203 this.clearSelections();
16204 var el = this.dataName ?
16205 this.el.child('.roo-tpl-' + this.dataName) :
16208 el.dom.removeChild(this.nodes[index]);
16209 this.updateIndexes(index);
16213 * Refresh an individual node.
16214 * @param {Number} index
16216 refreshNode : function(index){
16217 this.onUpdate(this.store, this.store.getAt(index));
16220 updateIndexes : function(startIndex, endIndex){
16221 var ns = this.nodes;
16222 startIndex = startIndex || 0;
16223 endIndex = endIndex || ns.length - 1;
16224 for(var i = startIndex; i <= endIndex; i++){
16225 ns[i].nodeIndex = i;
16230 * Changes the data store this view uses and refresh the view.
16231 * @param {Store} store
16233 setStore : function(store, initial){
16234 if(!initial && this.store){
16235 this.store.un("datachanged", this.refresh);
16236 this.store.un("add", this.onAdd);
16237 this.store.un("remove", this.onRemove);
16238 this.store.un("update", this.onUpdate);
16239 this.store.un("clear", this.refresh);
16240 this.store.un("beforeload", this.onBeforeLoad);
16241 this.store.un("load", this.onLoad);
16242 this.store.un("loadexception", this.onLoad);
16246 store.on("datachanged", this.refresh, this);
16247 store.on("add", this.onAdd, this);
16248 store.on("remove", this.onRemove, this);
16249 store.on("update", this.onUpdate, this);
16250 store.on("clear", this.refresh, this);
16251 store.on("beforeload", this.onBeforeLoad, this);
16252 store.on("load", this.onLoad, this);
16253 store.on("loadexception", this.onLoad, this);
16261 * onbeforeLoad - masks the loading area.
16264 onBeforeLoad : function(store,opts)
16266 //Roo.log('onBeforeLoad');
16268 this.el.update("");
16270 this.el.mask(this.mask ? this.mask : "Loading" );
16272 onLoad : function ()
16279 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16280 * @param {HTMLElement} node
16281 * @return {HTMLElement} The template node
16283 findItemFromChild : function(node){
16284 var el = this.dataName ?
16285 this.el.child('.roo-tpl-' + this.dataName,true) :
16288 if(!node || node.parentNode == el){
16291 var p = node.parentNode;
16292 while(p && p != el){
16293 if(p.parentNode == el){
16302 onClick : function(e){
16303 var item = this.findItemFromChild(e.getTarget());
16305 var index = this.indexOf(item);
16306 if(this.onItemClick(item, index, e) !== false){
16307 this.fireEvent("click", this, index, item, e);
16310 this.clearSelections();
16315 onContextMenu : function(e){
16316 var item = this.findItemFromChild(e.getTarget());
16318 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16323 onDblClick : function(e){
16324 var item = this.findItemFromChild(e.getTarget());
16326 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16330 onItemClick : function(item, index, e)
16332 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16335 if (this.toggleSelect) {
16336 var m = this.isSelected(item) ? 'unselect' : 'select';
16339 _t[m](item, true, false);
16342 if(this.multiSelect || this.singleSelect){
16343 if(this.multiSelect && e.shiftKey && this.lastSelection){
16344 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16346 this.select(item, this.multiSelect && e.ctrlKey);
16347 this.lastSelection = item;
16350 if(!this.tickable){
16351 e.preventDefault();
16359 * Get the number of selected nodes.
16362 getSelectionCount : function(){
16363 return this.selections.length;
16367 * Get the currently selected nodes.
16368 * @return {Array} An array of HTMLElements
16370 getSelectedNodes : function(){
16371 return this.selections;
16375 * Get the indexes of the selected nodes.
16378 getSelectedIndexes : function(){
16379 var indexes = [], s = this.selections;
16380 for(var i = 0, len = s.length; i < len; i++){
16381 indexes.push(s[i].nodeIndex);
16387 * Clear all selections
16388 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16390 clearSelections : function(suppressEvent){
16391 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16392 this.cmp.elements = this.selections;
16393 this.cmp.removeClass(this.selectedClass);
16394 this.selections = [];
16395 if(!suppressEvent){
16396 this.fireEvent("selectionchange", this, this.selections);
16402 * Returns true if the passed node is selected
16403 * @param {HTMLElement/Number} node The node or node index
16404 * @return {Boolean}
16406 isSelected : function(node){
16407 var s = this.selections;
16411 node = this.getNode(node);
16412 return s.indexOf(node) !== -1;
16417 * @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
16418 * @param {Boolean} keepExisting (optional) true to keep existing selections
16419 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16421 select : function(nodeInfo, keepExisting, suppressEvent){
16422 if(nodeInfo instanceof Array){
16424 this.clearSelections(true);
16426 for(var i = 0, len = nodeInfo.length; i < len; i++){
16427 this.select(nodeInfo[i], true, true);
16431 var node = this.getNode(nodeInfo);
16432 if(!node || this.isSelected(node)){
16433 return; // already selected.
16436 this.clearSelections(true);
16439 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16440 Roo.fly(node).addClass(this.selectedClass);
16441 this.selections.push(node);
16442 if(!suppressEvent){
16443 this.fireEvent("selectionchange", this, this.selections);
16451 * @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
16452 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16453 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16455 unselect : function(nodeInfo, keepExisting, suppressEvent)
16457 if(nodeInfo instanceof Array){
16458 Roo.each(this.selections, function(s) {
16459 this.unselect(s, nodeInfo);
16463 var node = this.getNode(nodeInfo);
16464 if(!node || !this.isSelected(node)){
16465 //Roo.log("not selected");
16466 return; // not selected.
16470 Roo.each(this.selections, function(s) {
16472 Roo.fly(node).removeClass(this.selectedClass);
16479 this.selections= ns;
16480 this.fireEvent("selectionchange", this, this.selections);
16484 * Gets a template node.
16485 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16486 * @return {HTMLElement} The node or null if it wasn't found
16488 getNode : function(nodeInfo){
16489 if(typeof nodeInfo == "string"){
16490 return document.getElementById(nodeInfo);
16491 }else if(typeof nodeInfo == "number"){
16492 return this.nodes[nodeInfo];
16498 * Gets a range template nodes.
16499 * @param {Number} startIndex
16500 * @param {Number} endIndex
16501 * @return {Array} An array of nodes
16503 getNodes : function(start, end){
16504 var ns = this.nodes;
16505 start = start || 0;
16506 end = typeof end == "undefined" ? ns.length - 1 : end;
16509 for(var i = start; i <= end; i++){
16513 for(var i = start; i >= end; i--){
16521 * Finds the index of the passed node
16522 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16523 * @return {Number} The index of the node or -1
16525 indexOf : function(node){
16526 node = this.getNode(node);
16527 if(typeof node.nodeIndex == "number"){
16528 return node.nodeIndex;
16530 var ns = this.nodes;
16531 for(var i = 0, len = ns.length; i < len; i++){
16542 * based on jquery fullcalendar
16546 Roo.bootstrap = Roo.bootstrap || {};
16548 * @class Roo.bootstrap.Calendar
16549 * @extends Roo.bootstrap.Component
16550 * Bootstrap Calendar class
16551 * @cfg {Boolean} loadMask (true|false) default false
16552 * @cfg {Object} header generate the user specific header of the calendar, default false
16555 * Create a new Container
16556 * @param {Object} config The config object
16561 Roo.bootstrap.Calendar = function(config){
16562 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16566 * Fires when a date is selected
16567 * @param {DatePicker} this
16568 * @param {Date} date The selected date
16572 * @event monthchange
16573 * Fires when the displayed month changes
16574 * @param {DatePicker} this
16575 * @param {Date} date The selected month
16577 'monthchange': true,
16579 * @event evententer
16580 * Fires when mouse over an event
16581 * @param {Calendar} this
16582 * @param {event} Event
16584 'evententer': true,
16586 * @event eventleave
16587 * Fires when the mouse leaves an
16588 * @param {Calendar} this
16591 'eventleave': true,
16593 * @event eventclick
16594 * Fires when the mouse click an
16595 * @param {Calendar} this
16604 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16607 * @cfg {Number} startDay
16608 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16616 getAutoCreate : function(){
16619 var fc_button = function(name, corner, style, content ) {
16620 return Roo.apply({},{
16622 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16624 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16627 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16638 style : 'width:100%',
16645 cls : 'fc-header-left',
16647 fc_button('prev', 'left', 'arrow', '‹' ),
16648 fc_button('next', 'right', 'arrow', '›' ),
16649 { tag: 'span', cls: 'fc-header-space' },
16650 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16658 cls : 'fc-header-center',
16662 cls: 'fc-header-title',
16665 html : 'month / year'
16673 cls : 'fc-header-right',
16675 /* fc_button('month', 'left', '', 'month' ),
16676 fc_button('week', '', '', 'week' ),
16677 fc_button('day', 'right', '', 'day' )
16689 header = this.header;
16692 var cal_heads = function() {
16694 // fixme - handle this.
16696 for (var i =0; i < Date.dayNames.length; i++) {
16697 var d = Date.dayNames[i];
16700 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16701 html : d.substring(0,3)
16705 ret[0].cls += ' fc-first';
16706 ret[6].cls += ' fc-last';
16709 var cal_cell = function(n) {
16712 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16717 cls: 'fc-day-number',
16721 cls: 'fc-day-content',
16725 style: 'position: relative;' // height: 17px;
16737 var cal_rows = function() {
16740 for (var r = 0; r < 6; r++) {
16747 for (var i =0; i < Date.dayNames.length; i++) {
16748 var d = Date.dayNames[i];
16749 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16752 row.cn[0].cls+=' fc-first';
16753 row.cn[0].cn[0].style = 'min-height:90px';
16754 row.cn[6].cls+=' fc-last';
16758 ret[0].cls += ' fc-first';
16759 ret[4].cls += ' fc-prev-last';
16760 ret[5].cls += ' fc-last';
16767 cls: 'fc-border-separate',
16768 style : 'width:100%',
16776 cls : 'fc-first fc-last',
16794 cls : 'fc-content',
16795 style : "position: relative;",
16798 cls : 'fc-view fc-view-month fc-grid',
16799 style : 'position: relative',
16800 unselectable : 'on',
16803 cls : 'fc-event-container',
16804 style : 'position:absolute;z-index:8;top:0;left:0;'
16822 initEvents : function()
16825 throw "can not find store for calendar";
16831 style: "text-align:center",
16835 style: "background-color:white;width:50%;margin:250 auto",
16839 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16850 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16852 var size = this.el.select('.fc-content', true).first().getSize();
16853 this.maskEl.setSize(size.width, size.height);
16854 this.maskEl.enableDisplayMode("block");
16855 if(!this.loadMask){
16856 this.maskEl.hide();
16859 this.store = Roo.factory(this.store, Roo.data);
16860 this.store.on('load', this.onLoad, this);
16861 this.store.on('beforeload', this.onBeforeLoad, this);
16865 this.cells = this.el.select('.fc-day',true);
16866 //Roo.log(this.cells);
16867 this.textNodes = this.el.query('.fc-day-number');
16868 this.cells.addClassOnOver('fc-state-hover');
16870 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16871 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16872 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16873 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16875 this.on('monthchange', this.onMonthChange, this);
16877 this.update(new Date().clearTime());
16880 resize : function() {
16881 var sz = this.el.getSize();
16883 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16884 this.el.select('.fc-day-content div',true).setHeight(34);
16889 showPrevMonth : function(e){
16890 this.update(this.activeDate.add("mo", -1));
16892 showToday : function(e){
16893 this.update(new Date().clearTime());
16896 showNextMonth : function(e){
16897 this.update(this.activeDate.add("mo", 1));
16901 showPrevYear : function(){
16902 this.update(this.activeDate.add("y", -1));
16906 showNextYear : function(){
16907 this.update(this.activeDate.add("y", 1));
16912 update : function(date)
16914 var vd = this.activeDate;
16915 this.activeDate = date;
16916 // if(vd && this.el){
16917 // var t = date.getTime();
16918 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16919 // Roo.log('using add remove');
16921 // this.fireEvent('monthchange', this, date);
16923 // this.cells.removeClass("fc-state-highlight");
16924 // this.cells.each(function(c){
16925 // if(c.dateValue == t){
16926 // c.addClass("fc-state-highlight");
16927 // setTimeout(function(){
16928 // try{c.dom.firstChild.focus();}catch(e){}
16938 var days = date.getDaysInMonth();
16940 var firstOfMonth = date.getFirstDateOfMonth();
16941 var startingPos = firstOfMonth.getDay()-this.startDay;
16943 if(startingPos < this.startDay){
16947 var pm = date.add(Date.MONTH, -1);
16948 var prevStart = pm.getDaysInMonth()-startingPos;
16950 this.cells = this.el.select('.fc-day',true);
16951 this.textNodes = this.el.query('.fc-day-number');
16952 this.cells.addClassOnOver('fc-state-hover');
16954 var cells = this.cells.elements;
16955 var textEls = this.textNodes;
16957 Roo.each(cells, function(cell){
16958 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16961 days += startingPos;
16963 // convert everything to numbers so it's fast
16964 var day = 86400000;
16965 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16968 //Roo.log(prevStart);
16970 var today = new Date().clearTime().getTime();
16971 var sel = date.clearTime().getTime();
16972 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16973 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16974 var ddMatch = this.disabledDatesRE;
16975 var ddText = this.disabledDatesText;
16976 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16977 var ddaysText = this.disabledDaysText;
16978 var format = this.format;
16980 var setCellClass = function(cal, cell){
16984 //Roo.log('set Cell Class');
16986 var t = d.getTime();
16990 cell.dateValue = t;
16992 cell.className += " fc-today";
16993 cell.className += " fc-state-highlight";
16994 cell.title = cal.todayText;
16997 // disable highlight in other month..
16998 //cell.className += " fc-state-highlight";
17003 cell.className = " fc-state-disabled";
17004 cell.title = cal.minText;
17008 cell.className = " fc-state-disabled";
17009 cell.title = cal.maxText;
17013 if(ddays.indexOf(d.getDay()) != -1){
17014 cell.title = ddaysText;
17015 cell.className = " fc-state-disabled";
17018 if(ddMatch && format){
17019 var fvalue = d.dateFormat(format);
17020 if(ddMatch.test(fvalue)){
17021 cell.title = ddText.replace("%0", fvalue);
17022 cell.className = " fc-state-disabled";
17026 if (!cell.initialClassName) {
17027 cell.initialClassName = cell.dom.className;
17030 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17035 for(; i < startingPos; i++) {
17036 textEls[i].innerHTML = (++prevStart);
17037 d.setDate(d.getDate()+1);
17039 cells[i].className = "fc-past fc-other-month";
17040 setCellClass(this, cells[i]);
17045 for(; i < days; i++){
17046 intDay = i - startingPos + 1;
17047 textEls[i].innerHTML = (intDay);
17048 d.setDate(d.getDate()+1);
17050 cells[i].className = ''; // "x-date-active";
17051 setCellClass(this, cells[i]);
17055 for(; i < 42; i++) {
17056 textEls[i].innerHTML = (++extraDays);
17057 d.setDate(d.getDate()+1);
17059 cells[i].className = "fc-future fc-other-month";
17060 setCellClass(this, cells[i]);
17063 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17065 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17067 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17068 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17070 if(totalRows != 6){
17071 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17072 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17075 this.fireEvent('monthchange', this, date);
17079 if(!this.internalRender){
17080 var main = this.el.dom.firstChild;
17081 var w = main.offsetWidth;
17082 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17083 Roo.fly(main).setWidth(w);
17084 this.internalRender = true;
17085 // opera does not respect the auto grow header center column
17086 // then, after it gets a width opera refuses to recalculate
17087 // without a second pass
17088 if(Roo.isOpera && !this.secondPass){
17089 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17090 this.secondPass = true;
17091 this.update.defer(10, this, [date]);
17098 findCell : function(dt) {
17099 dt = dt.clearTime().getTime();
17101 this.cells.each(function(c){
17102 //Roo.log("check " +c.dateValue + '?=' + dt);
17103 if(c.dateValue == dt){
17113 findCells : function(ev) {
17114 var s = ev.start.clone().clearTime().getTime();
17116 var e= ev.end.clone().clearTime().getTime();
17119 this.cells.each(function(c){
17120 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17122 if(c.dateValue > e){
17125 if(c.dateValue < s){
17134 // findBestRow: function(cells)
17138 // for (var i =0 ; i < cells.length;i++) {
17139 // ret = Math.max(cells[i].rows || 0,ret);
17146 addItem : function(ev)
17148 // look for vertical location slot in
17149 var cells = this.findCells(ev);
17151 // ev.row = this.findBestRow(cells);
17153 // work out the location.
17157 for(var i =0; i < cells.length; i++) {
17159 cells[i].row = cells[0].row;
17162 cells[i].row = cells[i].row + 1;
17172 if (crow.start.getY() == cells[i].getY()) {
17174 crow.end = cells[i];
17191 cells[0].events.push(ev);
17193 this.calevents.push(ev);
17196 clearEvents: function() {
17198 if(!this.calevents){
17202 Roo.each(this.cells.elements, function(c){
17208 Roo.each(this.calevents, function(e) {
17209 Roo.each(e.els, function(el) {
17210 el.un('mouseenter' ,this.onEventEnter, this);
17211 el.un('mouseleave' ,this.onEventLeave, this);
17216 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17222 renderEvents: function()
17226 this.cells.each(function(c) {
17235 if(c.row != c.events.length){
17236 r = 4 - (4 - (c.row - c.events.length));
17239 c.events = ev.slice(0, r);
17240 c.more = ev.slice(r);
17242 if(c.more.length && c.more.length == 1){
17243 c.events.push(c.more.pop());
17246 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17250 this.cells.each(function(c) {
17252 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17255 for (var e = 0; e < c.events.length; e++){
17256 var ev = c.events[e];
17257 var rows = ev.rows;
17259 for(var i = 0; i < rows.length; i++) {
17261 // how many rows should it span..
17264 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17265 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17267 unselectable : "on",
17270 cls: 'fc-event-inner',
17274 // cls: 'fc-event-time',
17275 // html : cells.length > 1 ? '' : ev.time
17279 cls: 'fc-event-title',
17280 html : String.format('{0}', ev.title)
17287 cls: 'ui-resizable-handle ui-resizable-e',
17288 html : '  '
17295 cfg.cls += ' fc-event-start';
17297 if ((i+1) == rows.length) {
17298 cfg.cls += ' fc-event-end';
17301 var ctr = _this.el.select('.fc-event-container',true).first();
17302 var cg = ctr.createChild(cfg);
17304 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17305 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17307 var r = (c.more.length) ? 1 : 0;
17308 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17309 cg.setWidth(ebox.right - sbox.x -2);
17311 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17312 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17313 cg.on('click', _this.onEventClick, _this, ev);
17324 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17325 style : 'position: absolute',
17326 unselectable : "on",
17329 cls: 'fc-event-inner',
17333 cls: 'fc-event-title',
17341 cls: 'ui-resizable-handle ui-resizable-e',
17342 html : '  '
17348 var ctr = _this.el.select('.fc-event-container',true).first();
17349 var cg = ctr.createChild(cfg);
17351 var sbox = c.select('.fc-day-content',true).first().getBox();
17352 var ebox = c.select('.fc-day-content',true).first().getBox();
17354 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17355 cg.setWidth(ebox.right - sbox.x -2);
17357 cg.on('click', _this.onMoreEventClick, _this, c.more);
17367 onEventEnter: function (e, el,event,d) {
17368 this.fireEvent('evententer', this, el, event);
17371 onEventLeave: function (e, el,event,d) {
17372 this.fireEvent('eventleave', this, el, event);
17375 onEventClick: function (e, el,event,d) {
17376 this.fireEvent('eventclick', this, el, event);
17379 onMonthChange: function () {
17383 onMoreEventClick: function(e, el, more)
17387 this.calpopover.placement = 'right';
17388 this.calpopover.setTitle('More');
17390 this.calpopover.setContent('');
17392 var ctr = this.calpopover.el.select('.popover-content', true).first();
17394 Roo.each(more, function(m){
17396 cls : 'fc-event-hori fc-event-draggable',
17399 var cg = ctr.createChild(cfg);
17401 cg.on('click', _this.onEventClick, _this, m);
17404 this.calpopover.show(el);
17409 onLoad: function ()
17411 this.calevents = [];
17414 if(this.store.getCount() > 0){
17415 this.store.data.each(function(d){
17418 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17419 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17420 time : d.data.start_time,
17421 title : d.data.title,
17422 description : d.data.description,
17423 venue : d.data.venue
17428 this.renderEvents();
17430 if(this.calevents.length && this.loadMask){
17431 this.maskEl.hide();
17435 onBeforeLoad: function()
17437 this.clearEvents();
17439 this.maskEl.show();
17453 * @class Roo.bootstrap.Popover
17454 * @extends Roo.bootstrap.Component
17455 * Bootstrap Popover class
17456 * @cfg {String} html contents of the popover (or false to use children..)
17457 * @cfg {String} title of popover (or false to hide)
17458 * @cfg {String} placement how it is placed
17459 * @cfg {String} trigger click || hover (or false to trigger manually)
17460 * @cfg {String} over what (parent or false to trigger manually.)
17461 * @cfg {Number} delay - delay before showing
17464 * Create a new Popover
17465 * @param {Object} config The config object
17468 Roo.bootstrap.Popover = function(config){
17469 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17475 * After the popover show
17477 * @param {Roo.bootstrap.Popover} this
17482 * After the popover hide
17484 * @param {Roo.bootstrap.Popover} this
17490 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17492 title: 'Fill in a title',
17495 placement : 'right',
17496 trigger : 'hover', // hover
17502 can_build_overlaid : false,
17504 getChildContainer : function()
17506 return this.el.select('.popover-content',true).first();
17509 getAutoCreate : function(){
17512 cls : 'popover roo-dynamic',
17513 style: 'display:block',
17519 cls : 'popover-inner',
17523 cls: 'popover-title',
17527 cls : 'popover-content',
17538 setTitle: function(str)
17541 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17543 setContent: function(str)
17546 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17548 // as it get's added to the bottom of the page.
17549 onRender : function(ct, position)
17551 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17553 var cfg = Roo.apply({}, this.getAutoCreate());
17557 cfg.cls += ' ' + this.cls;
17560 cfg.style = this.style;
17562 //Roo.log("adding to ");
17563 this.el = Roo.get(document.body).createChild(cfg, position);
17564 // Roo.log(this.el);
17569 initEvents : function()
17571 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17572 this.el.enableDisplayMode('block');
17574 if (this.over === false) {
17577 if (this.triggers === false) {
17580 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17581 var triggers = this.trigger ? this.trigger.split(' ') : [];
17582 Roo.each(triggers, function(trigger) {
17584 if (trigger == 'click') {
17585 on_el.on('click', this.toggle, this);
17586 } else if (trigger != 'manual') {
17587 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17588 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17590 on_el.on(eventIn ,this.enter, this);
17591 on_el.on(eventOut, this.leave, this);
17602 toggle : function () {
17603 this.hoverState == 'in' ? this.leave() : this.enter();
17606 enter : function () {
17608 clearTimeout(this.timeout);
17610 this.hoverState = 'in';
17612 if (!this.delay || !this.delay.show) {
17617 this.timeout = setTimeout(function () {
17618 if (_t.hoverState == 'in') {
17621 }, this.delay.show)
17624 leave : function() {
17625 clearTimeout(this.timeout);
17627 this.hoverState = 'out';
17629 if (!this.delay || !this.delay.hide) {
17634 this.timeout = setTimeout(function () {
17635 if (_t.hoverState == 'out') {
17638 }, this.delay.hide)
17641 show : function (on_el)
17644 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17648 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17649 if (this.html !== false) {
17650 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17652 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17653 if (!this.title.length) {
17654 this.el.select('.popover-title',true).hide();
17657 var placement = typeof this.placement == 'function' ?
17658 this.placement.call(this, this.el, on_el) :
17661 var autoToken = /\s?auto?\s?/i;
17662 var autoPlace = autoToken.test(placement);
17664 placement = placement.replace(autoToken, '') || 'top';
17668 //this.el.setXY([0,0]);
17670 this.el.dom.style.display='block';
17671 this.el.addClass(placement);
17673 //this.el.appendTo(on_el);
17675 var p = this.getPosition();
17676 var box = this.el.getBox();
17681 var align = Roo.bootstrap.Popover.alignment[placement];
17684 this.el.alignTo(on_el, align[0],align[1]);
17685 //var arrow = this.el.select('.arrow',true).first();
17686 //arrow.set(align[2],
17688 this.el.addClass('in');
17691 if (this.el.hasClass('fade')) {
17695 this.hoverState = 'in';
17697 this.fireEvent('show', this);
17702 this.el.setXY([0,0]);
17703 this.el.removeClass('in');
17705 this.hoverState = null;
17707 this.fireEvent('hide', this);
17712 Roo.bootstrap.Popover.alignment = {
17713 'left' : ['r-l', [-10,0], 'right'],
17714 'right' : ['l-r', [10,0], 'left'],
17715 'bottom' : ['t-b', [0,10], 'top'],
17716 'top' : [ 'b-t', [0,-10], 'bottom']
17727 * @class Roo.bootstrap.Progress
17728 * @extends Roo.bootstrap.Component
17729 * Bootstrap Progress class
17730 * @cfg {Boolean} striped striped of the progress bar
17731 * @cfg {Boolean} active animated of the progress bar
17735 * Create a new Progress
17736 * @param {Object} config The config object
17739 Roo.bootstrap.Progress = function(config){
17740 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17743 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17748 getAutoCreate : function(){
17756 cfg.cls += ' progress-striped';
17760 cfg.cls += ' active';
17779 * @class Roo.bootstrap.ProgressBar
17780 * @extends Roo.bootstrap.Component
17781 * Bootstrap ProgressBar class
17782 * @cfg {Number} aria_valuenow aria-value now
17783 * @cfg {Number} aria_valuemin aria-value min
17784 * @cfg {Number} aria_valuemax aria-value max
17785 * @cfg {String} label label for the progress bar
17786 * @cfg {String} panel (success | info | warning | danger )
17787 * @cfg {String} role role of the progress bar
17788 * @cfg {String} sr_only text
17792 * Create a new ProgressBar
17793 * @param {Object} config The config object
17796 Roo.bootstrap.ProgressBar = function(config){
17797 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17800 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17804 aria_valuemax : 100,
17810 getAutoCreate : function()
17815 cls: 'progress-bar',
17816 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17828 cfg.role = this.role;
17831 if(this.aria_valuenow){
17832 cfg['aria-valuenow'] = this.aria_valuenow;
17835 if(this.aria_valuemin){
17836 cfg['aria-valuemin'] = this.aria_valuemin;
17839 if(this.aria_valuemax){
17840 cfg['aria-valuemax'] = this.aria_valuemax;
17843 if(this.label && !this.sr_only){
17844 cfg.html = this.label;
17848 cfg.cls += ' progress-bar-' + this.panel;
17854 update : function(aria_valuenow)
17856 this.aria_valuenow = aria_valuenow;
17858 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17873 * @class Roo.bootstrap.TabGroup
17874 * @extends Roo.bootstrap.Column
17875 * Bootstrap Column class
17876 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17877 * @cfg {Boolean} carousel true to make the group behave like a carousel
17878 * @cfg {Boolean} bullets show bullets for the panels
17879 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17880 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17881 * @cfg {Boolean} showarrow (true|false) show arrow default true
17884 * Create a new TabGroup
17885 * @param {Object} config The config object
17888 Roo.bootstrap.TabGroup = function(config){
17889 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17891 this.navId = Roo.id();
17894 Roo.bootstrap.TabGroup.register(this);
17898 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17901 transition : false,
17906 slideOnTouch : false,
17909 getAutoCreate : function()
17911 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17913 cfg.cls += ' tab-content';
17915 if (this.carousel) {
17916 cfg.cls += ' carousel slide';
17919 cls : 'carousel-inner',
17923 if(this.bullets && !Roo.isTouch){
17926 cls : 'carousel-bullets',
17930 if(this.bullets_cls){
17931 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17938 cfg.cn[0].cn.push(bullets);
17941 if(this.showarrow){
17942 cfg.cn[0].cn.push({
17944 class : 'carousel-arrow',
17948 class : 'carousel-prev',
17952 class : 'fa fa-chevron-left'
17958 class : 'carousel-next',
17962 class : 'fa fa-chevron-right'
17975 initEvents: function()
17977 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17978 // this.el.on("touchstart", this.onTouchStart, this);
17981 if(this.autoslide){
17984 this.slideFn = window.setInterval(function() {
17985 _this.showPanelNext();
17989 if(this.showarrow){
17990 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17991 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17997 // onTouchStart : function(e, el, o)
17999 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18003 // this.showPanelNext();
18007 getChildContainer : function()
18009 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18013 * register a Navigation item
18014 * @param {Roo.bootstrap.NavItem} the navitem to add
18016 register : function(item)
18018 this.tabs.push( item);
18019 item.navId = this.navId; // not really needed..
18024 getActivePanel : function()
18027 Roo.each(this.tabs, function(t) {
18037 getPanelByName : function(n)
18040 Roo.each(this.tabs, function(t) {
18041 if (t.tabId == n) {
18049 indexOfPanel : function(p)
18052 Roo.each(this.tabs, function(t,i) {
18053 if (t.tabId == p.tabId) {
18062 * show a specific panel
18063 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18064 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18066 showPanel : function (pan)
18068 if(this.transition || typeof(pan) == 'undefined'){
18069 Roo.log("waiting for the transitionend");
18073 if (typeof(pan) == 'number') {
18074 pan = this.tabs[pan];
18077 if (typeof(pan) == 'string') {
18078 pan = this.getPanelByName(pan);
18081 var cur = this.getActivePanel();
18084 Roo.log('pan or acitve pan is undefined');
18088 if (pan.tabId == this.getActivePanel().tabId) {
18092 if (false === cur.fireEvent('beforedeactivate')) {
18096 if(this.bullets > 0 && !Roo.isTouch){
18097 this.setActiveBullet(this.indexOfPanel(pan));
18100 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18102 this.transition = true;
18103 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18104 var lr = dir == 'next' ? 'left' : 'right';
18105 pan.el.addClass(dir); // or prev
18106 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18107 cur.el.addClass(lr); // or right
18108 pan.el.addClass(lr);
18111 cur.el.on('transitionend', function() {
18112 Roo.log("trans end?");
18114 pan.el.removeClass([lr,dir]);
18115 pan.setActive(true);
18117 cur.el.removeClass([lr]);
18118 cur.setActive(false);
18120 _this.transition = false;
18122 }, this, { single: true } );
18127 cur.setActive(false);
18128 pan.setActive(true);
18133 showPanelNext : function()
18135 var i = this.indexOfPanel(this.getActivePanel());
18137 if (i >= this.tabs.length - 1 && !this.autoslide) {
18141 if (i >= this.tabs.length - 1 && this.autoslide) {
18145 this.showPanel(this.tabs[i+1]);
18148 showPanelPrev : function()
18150 var i = this.indexOfPanel(this.getActivePanel());
18152 if (i < 1 && !this.autoslide) {
18156 if (i < 1 && this.autoslide) {
18157 i = this.tabs.length;
18160 this.showPanel(this.tabs[i-1]);
18164 addBullet: function()
18166 if(!this.bullets || Roo.isTouch){
18169 var ctr = this.el.select('.carousel-bullets',true).first();
18170 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18171 var bullet = ctr.createChild({
18172 cls : 'bullet bullet-' + i
18173 },ctr.dom.lastChild);
18178 bullet.on('click', (function(e, el, o, ii, t){
18180 e.preventDefault();
18182 this.showPanel(ii);
18184 if(this.autoslide && this.slideFn){
18185 clearInterval(this.slideFn);
18186 this.slideFn = window.setInterval(function() {
18187 _this.showPanelNext();
18191 }).createDelegate(this, [i, bullet], true));
18196 setActiveBullet : function(i)
18202 Roo.each(this.el.select('.bullet', true).elements, function(el){
18203 el.removeClass('selected');
18206 var bullet = this.el.select('.bullet-' + i, true).first();
18212 bullet.addClass('selected');
18223 Roo.apply(Roo.bootstrap.TabGroup, {
18227 * register a Navigation Group
18228 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18230 register : function(navgrp)
18232 this.groups[navgrp.navId] = navgrp;
18236 * fetch a Navigation Group based on the navigation ID
18237 * if one does not exist , it will get created.
18238 * @param {string} the navgroup to add
18239 * @returns {Roo.bootstrap.NavGroup} the navgroup
18241 get: function(navId) {
18242 if (typeof(this.groups[navId]) == 'undefined') {
18243 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18245 return this.groups[navId] ;
18260 * @class Roo.bootstrap.TabPanel
18261 * @extends Roo.bootstrap.Component
18262 * Bootstrap TabPanel class
18263 * @cfg {Boolean} active panel active
18264 * @cfg {String} html panel content
18265 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18266 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18267 * @cfg {String} href click to link..
18271 * Create a new TabPanel
18272 * @param {Object} config The config object
18275 Roo.bootstrap.TabPanel = function(config){
18276 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18280 * Fires when the active status changes
18281 * @param {Roo.bootstrap.TabPanel} this
18282 * @param {Boolean} state the new state
18287 * @event beforedeactivate
18288 * Fires before a tab is de-activated - can be used to do validation on a form.
18289 * @param {Roo.bootstrap.TabPanel} this
18290 * @return {Boolean} false if there is an error
18293 'beforedeactivate': true
18296 this.tabId = this.tabId || Roo.id();
18300 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18308 getAutoCreate : function(){
18311 // item is needed for carousel - not sure if it has any effect otherwise
18312 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18313 html: this.html || ''
18317 cfg.cls += ' active';
18321 cfg.tabId = this.tabId;
18328 initEvents: function()
18330 var p = this.parent();
18332 this.navId = this.navId || p.navId;
18334 if (typeof(this.navId) != 'undefined') {
18335 // not really needed.. but just in case.. parent should be a NavGroup.
18336 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18340 var i = tg.tabs.length - 1;
18342 if(this.active && tg.bullets > 0 && i < tg.bullets){
18343 tg.setActiveBullet(i);
18347 this.el.on('click', this.onClick, this);
18350 this.el.on("touchstart", this.onTouchStart, this);
18351 this.el.on("touchmove", this.onTouchMove, this);
18352 this.el.on("touchend", this.onTouchEnd, this);
18357 onRender : function(ct, position)
18359 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18362 setActive : function(state)
18364 Roo.log("panel - set active " + this.tabId + "=" + state);
18366 this.active = state;
18368 this.el.removeClass('active');
18370 } else if (!this.el.hasClass('active')) {
18371 this.el.addClass('active');
18374 this.fireEvent('changed', this, state);
18377 onClick : function(e)
18379 e.preventDefault();
18381 if(!this.href.length){
18385 window.location.href = this.href;
18394 onTouchStart : function(e)
18396 this.swiping = false;
18398 this.startX = e.browserEvent.touches[0].clientX;
18399 this.startY = e.browserEvent.touches[0].clientY;
18402 onTouchMove : function(e)
18404 this.swiping = true;
18406 this.endX = e.browserEvent.touches[0].clientX;
18407 this.endY = e.browserEvent.touches[0].clientY;
18410 onTouchEnd : function(e)
18417 var tabGroup = this.parent();
18419 if(this.endX > this.startX){ // swiping right
18420 tabGroup.showPanelPrev();
18424 if(this.startX > this.endX){ // swiping left
18425 tabGroup.showPanelNext();
18444 * @class Roo.bootstrap.DateField
18445 * @extends Roo.bootstrap.Input
18446 * Bootstrap DateField class
18447 * @cfg {Number} weekStart default 0
18448 * @cfg {String} viewMode default empty, (months|years)
18449 * @cfg {String} minViewMode default empty, (months|years)
18450 * @cfg {Number} startDate default -Infinity
18451 * @cfg {Number} endDate default Infinity
18452 * @cfg {Boolean} todayHighlight default false
18453 * @cfg {Boolean} todayBtn default false
18454 * @cfg {Boolean} calendarWeeks default false
18455 * @cfg {Object} daysOfWeekDisabled default empty
18456 * @cfg {Boolean} singleMode default false (true | false)
18458 * @cfg {Boolean} keyboardNavigation default true
18459 * @cfg {String} language default en
18462 * Create a new DateField
18463 * @param {Object} config The config object
18466 Roo.bootstrap.DateField = function(config){
18467 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18471 * Fires when this field show.
18472 * @param {Roo.bootstrap.DateField} this
18473 * @param {Mixed} date The date value
18478 * Fires when this field hide.
18479 * @param {Roo.bootstrap.DateField} this
18480 * @param {Mixed} date The date value
18485 * Fires when select a date.
18486 * @param {Roo.bootstrap.DateField} this
18487 * @param {Mixed} date The date value
18491 * @event beforeselect
18492 * Fires when before select a date.
18493 * @param {Roo.bootstrap.DateField} this
18494 * @param {Mixed} date The date value
18496 beforeselect : true
18500 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18503 * @cfg {String} format
18504 * The default date format string which can be overriden for localization support. The format must be
18505 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18509 * @cfg {String} altFormats
18510 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18511 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18513 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18521 todayHighlight : false,
18527 keyboardNavigation: true,
18529 calendarWeeks: false,
18531 startDate: -Infinity,
18535 daysOfWeekDisabled: [],
18539 singleMode : false,
18541 UTCDate: function()
18543 return new Date(Date.UTC.apply(Date, arguments));
18546 UTCToday: function()
18548 var today = new Date();
18549 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18552 getDate: function() {
18553 var d = this.getUTCDate();
18554 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18557 getUTCDate: function() {
18561 setDate: function(d) {
18562 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18565 setUTCDate: function(d) {
18567 this.setValue(this.formatDate(this.date));
18570 onRender: function(ct, position)
18573 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18575 this.language = this.language || 'en';
18576 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18577 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18579 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18580 this.format = this.format || 'm/d/y';
18581 this.isInline = false;
18582 this.isInput = true;
18583 this.component = this.el.select('.add-on', true).first() || false;
18584 this.component = (this.component && this.component.length === 0) ? false : this.component;
18585 this.hasInput = this.component && this.inputEl().length;
18587 if (typeof(this.minViewMode === 'string')) {
18588 switch (this.minViewMode) {
18590 this.minViewMode = 1;
18593 this.minViewMode = 2;
18596 this.minViewMode = 0;
18601 if (typeof(this.viewMode === 'string')) {
18602 switch (this.viewMode) {
18615 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18617 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18619 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18621 this.picker().on('mousedown', this.onMousedown, this);
18622 this.picker().on('click', this.onClick, this);
18624 this.picker().addClass('datepicker-dropdown');
18626 this.startViewMode = this.viewMode;
18628 if(this.singleMode){
18629 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18630 v.setVisibilityMode(Roo.Element.DISPLAY);
18634 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18635 v.setStyle('width', '189px');
18639 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18640 if(!this.calendarWeeks){
18645 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18646 v.attr('colspan', function(i, val){
18647 return parseInt(val) + 1;
18652 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18654 this.setStartDate(this.startDate);
18655 this.setEndDate(this.endDate);
18657 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18664 if(this.isInline) {
18669 picker : function()
18671 return this.pickerEl;
18672 // return this.el.select('.datepicker', true).first();
18675 fillDow: function()
18677 var dowCnt = this.weekStart;
18686 if(this.calendarWeeks){
18694 while (dowCnt < this.weekStart + 7) {
18698 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18702 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18705 fillMonths: function()
18708 var months = this.picker().select('>.datepicker-months td', true).first();
18710 months.dom.innerHTML = '';
18716 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18719 months.createChild(month);
18726 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;
18728 if (this.date < this.startDate) {
18729 this.viewDate = new Date(this.startDate);
18730 } else if (this.date > this.endDate) {
18731 this.viewDate = new Date(this.endDate);
18733 this.viewDate = new Date(this.date);
18741 var d = new Date(this.viewDate),
18742 year = d.getUTCFullYear(),
18743 month = d.getUTCMonth(),
18744 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18745 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18746 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18747 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18748 currentDate = this.date && this.date.valueOf(),
18749 today = this.UTCToday();
18751 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18753 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18755 // this.picker.select('>tfoot th.today').
18756 // .text(dates[this.language].today)
18757 // .toggle(this.todayBtn !== false);
18759 this.updateNavArrows();
18762 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18764 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18766 prevMonth.setUTCDate(day);
18768 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18770 var nextMonth = new Date(prevMonth);
18772 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18774 nextMonth = nextMonth.valueOf();
18776 var fillMonths = false;
18778 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18780 while(prevMonth.valueOf() <= nextMonth) {
18783 if (prevMonth.getUTCDay() === this.weekStart) {
18785 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18793 if(this.calendarWeeks){
18794 // ISO 8601: First week contains first thursday.
18795 // ISO also states week starts on Monday, but we can be more abstract here.
18797 // Start of current week: based on weekstart/current date
18798 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18799 // Thursday of this week
18800 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18801 // First Thursday of year, year from thursday
18802 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18803 // Calendar week: ms between thursdays, div ms per day, div 7 days
18804 calWeek = (th - yth) / 864e5 / 7 + 1;
18806 fillMonths.cn.push({
18814 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18816 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18819 if (this.todayHighlight &&
18820 prevMonth.getUTCFullYear() == today.getFullYear() &&
18821 prevMonth.getUTCMonth() == today.getMonth() &&
18822 prevMonth.getUTCDate() == today.getDate()) {
18823 clsName += ' today';
18826 if (currentDate && prevMonth.valueOf() === currentDate) {
18827 clsName += ' active';
18830 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18831 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18832 clsName += ' disabled';
18835 fillMonths.cn.push({
18837 cls: 'day ' + clsName,
18838 html: prevMonth.getDate()
18841 prevMonth.setDate(prevMonth.getDate()+1);
18844 var currentYear = this.date && this.date.getUTCFullYear();
18845 var currentMonth = this.date && this.date.getUTCMonth();
18847 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18849 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18850 v.removeClass('active');
18852 if(currentYear === year && k === currentMonth){
18853 v.addClass('active');
18856 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18857 v.addClass('disabled');
18863 year = parseInt(year/10, 10) * 10;
18865 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18867 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18870 for (var i = -1; i < 11; i++) {
18871 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18873 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18881 showMode: function(dir)
18884 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18887 Roo.each(this.picker().select('>div',true).elements, function(v){
18888 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18891 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18896 if(this.isInline) {
18900 this.picker().removeClass(['bottom', 'top']);
18902 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18904 * place to the top of element!
18908 this.picker().addClass('top');
18909 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18914 this.picker().addClass('bottom');
18916 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18919 parseDate : function(value)
18921 if(!value || value instanceof Date){
18924 var v = Date.parseDate(value, this.format);
18925 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18926 v = Date.parseDate(value, 'Y-m-d');
18928 if(!v && this.altFormats){
18929 if(!this.altFormatsArray){
18930 this.altFormatsArray = this.altFormats.split("|");
18932 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18933 v = Date.parseDate(value, this.altFormatsArray[i]);
18939 formatDate : function(date, fmt)
18941 return (!date || !(date instanceof Date)) ?
18942 date : date.dateFormat(fmt || this.format);
18945 onFocus : function()
18947 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18951 onBlur : function()
18953 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18955 var d = this.inputEl().getValue();
18962 showPopup : function()
18964 this.picker().show();
18968 this.fireEvent('showpopup', this, this.date);
18971 hidePopup : function()
18973 if(this.isInline) {
18976 this.picker().hide();
18977 this.viewMode = this.startViewMode;
18980 this.fireEvent('hidepopup', this, this.date);
18984 onMousedown: function(e)
18986 e.stopPropagation();
18987 e.preventDefault();
18992 Roo.bootstrap.DateField.superclass.keyup.call(this);
18996 setValue: function(v)
18998 if(this.fireEvent('beforeselect', this, v) !== false){
18999 var d = new Date(this.parseDate(v) ).clearTime();
19001 if(isNaN(d.getTime())){
19002 this.date = this.viewDate = '';
19003 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19007 v = this.formatDate(d);
19009 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19011 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19015 this.fireEvent('select', this, this.date);
19019 getValue: function()
19021 return this.formatDate(this.date);
19024 fireKey: function(e)
19026 if (!this.picker().isVisible()){
19027 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19033 var dateChanged = false,
19035 newDate, newViewDate;
19040 e.preventDefault();
19044 if (!this.keyboardNavigation) {
19047 dir = e.keyCode == 37 ? -1 : 1;
19050 newDate = this.moveYear(this.date, dir);
19051 newViewDate = this.moveYear(this.viewDate, dir);
19052 } else if (e.shiftKey){
19053 newDate = this.moveMonth(this.date, dir);
19054 newViewDate = this.moveMonth(this.viewDate, dir);
19056 newDate = new Date(this.date);
19057 newDate.setUTCDate(this.date.getUTCDate() + dir);
19058 newViewDate = new Date(this.viewDate);
19059 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19061 if (this.dateWithinRange(newDate)){
19062 this.date = newDate;
19063 this.viewDate = newViewDate;
19064 this.setValue(this.formatDate(this.date));
19066 e.preventDefault();
19067 dateChanged = true;
19072 if (!this.keyboardNavigation) {
19075 dir = e.keyCode == 38 ? -1 : 1;
19077 newDate = this.moveYear(this.date, dir);
19078 newViewDate = this.moveYear(this.viewDate, dir);
19079 } else if (e.shiftKey){
19080 newDate = this.moveMonth(this.date, dir);
19081 newViewDate = this.moveMonth(this.viewDate, dir);
19083 newDate = new Date(this.date);
19084 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19085 newViewDate = new Date(this.viewDate);
19086 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19088 if (this.dateWithinRange(newDate)){
19089 this.date = newDate;
19090 this.viewDate = newViewDate;
19091 this.setValue(this.formatDate(this.date));
19093 e.preventDefault();
19094 dateChanged = true;
19098 this.setValue(this.formatDate(this.date));
19100 e.preventDefault();
19103 this.setValue(this.formatDate(this.date));
19117 onClick: function(e)
19119 e.stopPropagation();
19120 e.preventDefault();
19122 var target = e.getTarget();
19124 if(target.nodeName.toLowerCase() === 'i'){
19125 target = Roo.get(target).dom.parentNode;
19128 var nodeName = target.nodeName;
19129 var className = target.className;
19130 var html = target.innerHTML;
19131 //Roo.log(nodeName);
19133 switch(nodeName.toLowerCase()) {
19135 switch(className) {
19141 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19142 switch(this.viewMode){
19144 this.viewDate = this.moveMonth(this.viewDate, dir);
19148 this.viewDate = this.moveYear(this.viewDate, dir);
19154 var date = new Date();
19155 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19157 this.setValue(this.formatDate(this.date));
19164 if (className.indexOf('disabled') < 0) {
19165 this.viewDate.setUTCDate(1);
19166 if (className.indexOf('month') > -1) {
19167 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19169 var year = parseInt(html, 10) || 0;
19170 this.viewDate.setUTCFullYear(year);
19174 if(this.singleMode){
19175 this.setValue(this.formatDate(this.viewDate));
19186 //Roo.log(className);
19187 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19188 var day = parseInt(html, 10) || 1;
19189 var year = this.viewDate.getUTCFullYear(),
19190 month = this.viewDate.getUTCMonth();
19192 if (className.indexOf('old') > -1) {
19199 } else if (className.indexOf('new') > -1) {
19207 //Roo.log([year,month,day]);
19208 this.date = this.UTCDate(year, month, day,0,0,0,0);
19209 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19211 //Roo.log(this.formatDate(this.date));
19212 this.setValue(this.formatDate(this.date));
19219 setStartDate: function(startDate)
19221 this.startDate = startDate || -Infinity;
19222 if (this.startDate !== -Infinity) {
19223 this.startDate = this.parseDate(this.startDate);
19226 this.updateNavArrows();
19229 setEndDate: function(endDate)
19231 this.endDate = endDate || Infinity;
19232 if (this.endDate !== Infinity) {
19233 this.endDate = this.parseDate(this.endDate);
19236 this.updateNavArrows();
19239 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19241 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19242 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19243 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19245 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19246 return parseInt(d, 10);
19249 this.updateNavArrows();
19252 updateNavArrows: function()
19254 if(this.singleMode){
19258 var d = new Date(this.viewDate),
19259 year = d.getUTCFullYear(),
19260 month = d.getUTCMonth();
19262 Roo.each(this.picker().select('.prev', true).elements, function(v){
19264 switch (this.viewMode) {
19267 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19273 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19280 Roo.each(this.picker().select('.next', true).elements, function(v){
19282 switch (this.viewMode) {
19285 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19291 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19299 moveMonth: function(date, dir)
19304 var new_date = new Date(date.valueOf()),
19305 day = new_date.getUTCDate(),
19306 month = new_date.getUTCMonth(),
19307 mag = Math.abs(dir),
19309 dir = dir > 0 ? 1 : -1;
19312 // If going back one month, make sure month is not current month
19313 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19315 return new_date.getUTCMonth() == month;
19317 // If going forward one month, make sure month is as expected
19318 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19320 return new_date.getUTCMonth() != new_month;
19322 new_month = month + dir;
19323 new_date.setUTCMonth(new_month);
19324 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19325 if (new_month < 0 || new_month > 11) {
19326 new_month = (new_month + 12) % 12;
19329 // For magnitudes >1, move one month at a time...
19330 for (var i=0; i<mag; i++) {
19331 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19332 new_date = this.moveMonth(new_date, dir);
19334 // ...then reset the day, keeping it in the new month
19335 new_month = new_date.getUTCMonth();
19336 new_date.setUTCDate(day);
19338 return new_month != new_date.getUTCMonth();
19341 // Common date-resetting loop -- if date is beyond end of month, make it
19344 new_date.setUTCDate(--day);
19345 new_date.setUTCMonth(new_month);
19350 moveYear: function(date, dir)
19352 return this.moveMonth(date, dir*12);
19355 dateWithinRange: function(date)
19357 return date >= this.startDate && date <= this.endDate;
19363 this.picker().remove();
19366 validateValue : function(value)
19368 if(this.getVisibilityEl().hasClass('hidden')){
19372 if(value.length < 1) {
19373 if(this.allowBlank){
19379 if(value.length < this.minLength){
19382 if(value.length > this.maxLength){
19386 var vt = Roo.form.VTypes;
19387 if(!vt[this.vtype](value, this)){
19391 if(typeof this.validator == "function"){
19392 var msg = this.validator(value);
19398 if(this.regex && !this.regex.test(value)){
19402 if(typeof(this.parseDate(value)) == 'undefined'){
19406 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19410 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19420 this.date = this.viewDate = '';
19422 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19427 Roo.apply(Roo.bootstrap.DateField, {
19438 html: '<i class="fa fa-arrow-left"/>'
19448 html: '<i class="fa fa-arrow-right"/>'
19490 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19491 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19492 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19493 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19494 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19507 navFnc: 'FullYear',
19512 navFnc: 'FullYear',
19517 Roo.apply(Roo.bootstrap.DateField, {
19521 cls: 'datepicker dropdown-menu roo-dynamic',
19525 cls: 'datepicker-days',
19529 cls: 'table-condensed',
19531 Roo.bootstrap.DateField.head,
19535 Roo.bootstrap.DateField.footer
19542 cls: 'datepicker-months',
19546 cls: 'table-condensed',
19548 Roo.bootstrap.DateField.head,
19549 Roo.bootstrap.DateField.content,
19550 Roo.bootstrap.DateField.footer
19557 cls: 'datepicker-years',
19561 cls: 'table-condensed',
19563 Roo.bootstrap.DateField.head,
19564 Roo.bootstrap.DateField.content,
19565 Roo.bootstrap.DateField.footer
19584 * @class Roo.bootstrap.TimeField
19585 * @extends Roo.bootstrap.Input
19586 * Bootstrap DateField class
19590 * Create a new TimeField
19591 * @param {Object} config The config object
19594 Roo.bootstrap.TimeField = function(config){
19595 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19599 * Fires when this field show.
19600 * @param {Roo.bootstrap.DateField} thisthis
19601 * @param {Mixed} date The date value
19606 * Fires when this field hide.
19607 * @param {Roo.bootstrap.DateField} this
19608 * @param {Mixed} date The date value
19613 * Fires when select a date.
19614 * @param {Roo.bootstrap.DateField} this
19615 * @param {Mixed} date The date value
19621 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19624 * @cfg {String} format
19625 * The default time format string which can be overriden for localization support. The format must be
19626 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19630 onRender: function(ct, position)
19633 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19635 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19637 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19639 this.pop = this.picker().select('>.datepicker-time',true).first();
19640 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19642 this.picker().on('mousedown', this.onMousedown, this);
19643 this.picker().on('click', this.onClick, this);
19645 this.picker().addClass('datepicker-dropdown');
19650 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19651 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19652 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19653 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19654 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19655 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19659 fireKey: function(e){
19660 if (!this.picker().isVisible()){
19661 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19667 e.preventDefault();
19675 this.onTogglePeriod();
19678 this.onIncrementMinutes();
19681 this.onDecrementMinutes();
19690 onClick: function(e) {
19691 e.stopPropagation();
19692 e.preventDefault();
19695 picker : function()
19697 return this.el.select('.datepicker', true).first();
19700 fillTime: function()
19702 var time = this.pop.select('tbody', true).first();
19704 time.dom.innerHTML = '';
19719 cls: 'hours-up glyphicon glyphicon-chevron-up'
19739 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19760 cls: 'timepicker-hour',
19775 cls: 'timepicker-minute',
19790 cls: 'btn btn-primary period',
19812 cls: 'hours-down glyphicon glyphicon-chevron-down'
19832 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19850 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19857 var hours = this.time.getHours();
19858 var minutes = this.time.getMinutes();
19871 hours = hours - 12;
19875 hours = '0' + hours;
19879 minutes = '0' + minutes;
19882 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19883 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19884 this.pop.select('button', true).first().dom.innerHTML = period;
19890 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19892 var cls = ['bottom'];
19894 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19901 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19906 this.picker().addClass(cls.join('-'));
19910 Roo.each(cls, function(c){
19912 _this.picker().setTop(_this.inputEl().getHeight());
19916 _this.picker().setTop(0 - _this.picker().getHeight());
19921 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19925 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19932 onFocus : function()
19934 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19938 onBlur : function()
19940 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19946 this.picker().show();
19951 this.fireEvent('show', this, this.date);
19956 this.picker().hide();
19959 this.fireEvent('hide', this, this.date);
19962 setTime : function()
19965 this.setValue(this.time.format(this.format));
19967 this.fireEvent('select', this, this.date);
19972 onMousedown: function(e){
19973 e.stopPropagation();
19974 e.preventDefault();
19977 onIncrementHours: function()
19979 Roo.log('onIncrementHours');
19980 this.time = this.time.add(Date.HOUR, 1);
19985 onDecrementHours: function()
19987 Roo.log('onDecrementHours');
19988 this.time = this.time.add(Date.HOUR, -1);
19992 onIncrementMinutes: function()
19994 Roo.log('onIncrementMinutes');
19995 this.time = this.time.add(Date.MINUTE, 1);
19999 onDecrementMinutes: function()
20001 Roo.log('onDecrementMinutes');
20002 this.time = this.time.add(Date.MINUTE, -1);
20006 onTogglePeriod: function()
20008 Roo.log('onTogglePeriod');
20009 this.time = this.time.add(Date.HOUR, 12);
20016 Roo.apply(Roo.bootstrap.TimeField, {
20046 cls: 'btn btn-info ok',
20058 Roo.apply(Roo.bootstrap.TimeField, {
20062 cls: 'datepicker dropdown-menu',
20066 cls: 'datepicker-time',
20070 cls: 'table-condensed',
20072 Roo.bootstrap.TimeField.content,
20073 Roo.bootstrap.TimeField.footer
20092 * @class Roo.bootstrap.MonthField
20093 * @extends Roo.bootstrap.Input
20094 * Bootstrap MonthField class
20096 * @cfg {String} language default en
20099 * Create a new MonthField
20100 * @param {Object} config The config object
20103 Roo.bootstrap.MonthField = function(config){
20104 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20109 * Fires when this field show.
20110 * @param {Roo.bootstrap.MonthField} this
20111 * @param {Mixed} date The date value
20116 * Fires when this field hide.
20117 * @param {Roo.bootstrap.MonthField} this
20118 * @param {Mixed} date The date value
20123 * Fires when select a date.
20124 * @param {Roo.bootstrap.MonthField} this
20125 * @param {String} oldvalue The old value
20126 * @param {String} newvalue The new value
20132 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20134 onRender: function(ct, position)
20137 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20139 this.language = this.language || 'en';
20140 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20141 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20143 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20144 this.isInline = false;
20145 this.isInput = true;
20146 this.component = this.el.select('.add-on', true).first() || false;
20147 this.component = (this.component && this.component.length === 0) ? false : this.component;
20148 this.hasInput = this.component && this.inputEL().length;
20150 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20152 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20154 this.picker().on('mousedown', this.onMousedown, this);
20155 this.picker().on('click', this.onClick, this);
20157 this.picker().addClass('datepicker-dropdown');
20159 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20160 v.setStyle('width', '189px');
20167 if(this.isInline) {
20173 setValue: function(v, suppressEvent)
20175 var o = this.getValue();
20177 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20181 if(suppressEvent !== true){
20182 this.fireEvent('select', this, o, v);
20187 getValue: function()
20192 onClick: function(e)
20194 e.stopPropagation();
20195 e.preventDefault();
20197 var target = e.getTarget();
20199 if(target.nodeName.toLowerCase() === 'i'){
20200 target = Roo.get(target).dom.parentNode;
20203 var nodeName = target.nodeName;
20204 var className = target.className;
20205 var html = target.innerHTML;
20207 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20211 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20213 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20219 picker : function()
20221 return this.pickerEl;
20224 fillMonths: function()
20227 var months = this.picker().select('>.datepicker-months td', true).first();
20229 months.dom.innerHTML = '';
20235 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20238 months.createChild(month);
20247 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20248 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20251 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20252 e.removeClass('active');
20254 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20255 e.addClass('active');
20262 if(this.isInline) {
20266 this.picker().removeClass(['bottom', 'top']);
20268 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20270 * place to the top of element!
20274 this.picker().addClass('top');
20275 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20280 this.picker().addClass('bottom');
20282 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20285 onFocus : function()
20287 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20291 onBlur : function()
20293 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20295 var d = this.inputEl().getValue();
20304 this.picker().show();
20305 this.picker().select('>.datepicker-months', true).first().show();
20309 this.fireEvent('show', this, this.date);
20314 if(this.isInline) {
20317 this.picker().hide();
20318 this.fireEvent('hide', this, this.date);
20322 onMousedown: function(e)
20324 e.stopPropagation();
20325 e.preventDefault();
20330 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20334 fireKey: function(e)
20336 if (!this.picker().isVisible()){
20337 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20348 e.preventDefault();
20352 dir = e.keyCode == 37 ? -1 : 1;
20354 this.vIndex = this.vIndex + dir;
20356 if(this.vIndex < 0){
20360 if(this.vIndex > 11){
20364 if(isNaN(this.vIndex)){
20368 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20374 dir = e.keyCode == 38 ? -1 : 1;
20376 this.vIndex = this.vIndex + dir * 4;
20378 if(this.vIndex < 0){
20382 if(this.vIndex > 11){
20386 if(isNaN(this.vIndex)){
20390 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20395 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20396 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20400 e.preventDefault();
20403 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20404 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20420 this.picker().remove();
20425 Roo.apply(Roo.bootstrap.MonthField, {
20444 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20445 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20450 Roo.apply(Roo.bootstrap.MonthField, {
20454 cls: 'datepicker dropdown-menu roo-dynamic',
20458 cls: 'datepicker-months',
20462 cls: 'table-condensed',
20464 Roo.bootstrap.DateField.content
20484 * @class Roo.bootstrap.CheckBox
20485 * @extends Roo.bootstrap.Input
20486 * Bootstrap CheckBox class
20488 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20489 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20490 * @cfg {String} boxLabel The text that appears beside the checkbox
20491 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20492 * @cfg {Boolean} checked initnal the element
20493 * @cfg {Boolean} inline inline the element (default false)
20494 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20495 * @cfg {String} tooltip label tooltip
20498 * Create a new CheckBox
20499 * @param {Object} config The config object
20502 Roo.bootstrap.CheckBox = function(config){
20503 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20508 * Fires when the element is checked or unchecked.
20509 * @param {Roo.bootstrap.CheckBox} this This input
20510 * @param {Boolean} checked The new checked value
20515 * Fires when the element is click.
20516 * @param {Roo.bootstrap.CheckBox} this This input
20523 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20525 inputType: 'checkbox',
20534 getAutoCreate : function()
20536 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20542 cfg.cls = 'form-group ' + this.inputType; //input-group
20545 cfg.cls += ' ' + this.inputType + '-inline';
20551 type : this.inputType,
20552 value : this.inputValue,
20553 cls : 'roo-' + this.inputType, //'form-box',
20554 placeholder : this.placeholder || ''
20558 if(this.inputType != 'radio'){
20562 cls : 'roo-hidden-value',
20563 value : this.checked ? this.inputValue : this.valueOff
20568 if (this.weight) { // Validity check?
20569 cfg.cls += " " + this.inputType + "-" + this.weight;
20572 if (this.disabled) {
20573 input.disabled=true;
20577 input.checked = this.checked;
20582 input.name = this.name;
20584 if(this.inputType != 'radio'){
20585 hidden.name = this.name;
20586 input.name = '_hidden_' + this.name;
20591 input.cls += ' input-' + this.size;
20596 ['xs','sm','md','lg'].map(function(size){
20597 if (settings[size]) {
20598 cfg.cls += ' col-' + size + '-' + settings[size];
20602 var inputblock = input;
20604 if (this.before || this.after) {
20607 cls : 'input-group',
20612 inputblock.cn.push({
20614 cls : 'input-group-addon',
20619 inputblock.cn.push(input);
20621 if(this.inputType != 'radio'){
20622 inputblock.cn.push(hidden);
20626 inputblock.cn.push({
20628 cls : 'input-group-addon',
20635 if (align ==='left' && this.fieldLabel.length) {
20636 // Roo.log("left and has label");
20641 cls : 'control-label',
20642 html : this.fieldLabel
20652 if(this.labelWidth > 12){
20653 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20656 if(this.labelWidth < 13 && this.labelmd == 0){
20657 this.labelmd = this.labelWidth;
20660 if(this.labellg > 0){
20661 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20662 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20665 if(this.labelmd > 0){
20666 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20667 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20670 if(this.labelsm > 0){
20671 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20672 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20675 if(this.labelxs > 0){
20676 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20677 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20680 } else if ( this.fieldLabel.length) {
20681 // Roo.log(" label");
20685 tag: this.boxLabel ? 'span' : 'label',
20687 cls: 'control-label box-input-label',
20688 //cls : 'input-group-addon',
20689 html : this.fieldLabel
20698 // Roo.log(" no label && no align");
20699 cfg.cn = [ inputblock ] ;
20705 var boxLabelCfg = {
20707 //'for': id, // box label is handled by onclick - so no for...
20709 html: this.boxLabel
20713 boxLabelCfg.tooltip = this.tooltip;
20716 cfg.cn.push(boxLabelCfg);
20719 if(this.inputType != 'radio'){
20720 cfg.cn.push(hidden);
20728 * return the real input element.
20730 inputEl: function ()
20732 return this.el.select('input.roo-' + this.inputType,true).first();
20734 hiddenEl: function ()
20736 return this.el.select('input.roo-hidden-value',true).first();
20739 labelEl: function()
20741 return this.el.select('label.control-label',true).first();
20743 /* depricated... */
20747 return this.labelEl();
20750 boxLabelEl: function()
20752 return this.el.select('label.box-label',true).first();
20755 initEvents : function()
20757 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20759 this.inputEl().on('click', this.onClick, this);
20761 if (this.boxLabel) {
20762 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20765 this.startValue = this.getValue();
20768 Roo.bootstrap.CheckBox.register(this);
20772 onClick : function(e)
20774 if(this.fireEvent('click', this, e) !== false){
20775 this.setChecked(!this.checked);
20780 setChecked : function(state,suppressEvent)
20782 this.startValue = this.getValue();
20784 if(this.inputType == 'radio'){
20786 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20787 e.dom.checked = false;
20790 this.inputEl().dom.checked = true;
20792 this.inputEl().dom.value = this.inputValue;
20794 if(suppressEvent !== true){
20795 this.fireEvent('check', this, true);
20803 this.checked = state;
20805 this.inputEl().dom.checked = state;
20808 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20810 if(suppressEvent !== true){
20811 this.fireEvent('check', this, state);
20817 getValue : function()
20819 if(this.inputType == 'radio'){
20820 return this.getGroupValue();
20823 return this.hiddenEl().dom.value;
20827 getGroupValue : function()
20829 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20833 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20836 setValue : function(v,suppressEvent)
20838 if(this.inputType == 'radio'){
20839 this.setGroupValue(v, suppressEvent);
20843 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20848 setGroupValue : function(v, suppressEvent)
20850 this.startValue = this.getValue();
20852 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20853 e.dom.checked = false;
20855 if(e.dom.value == v){
20856 e.dom.checked = true;
20860 if(suppressEvent !== true){
20861 this.fireEvent('check', this, true);
20869 validate : function()
20871 if(this.getVisibilityEl().hasClass('hidden')){
20877 (this.inputType == 'radio' && this.validateRadio()) ||
20878 (this.inputType == 'checkbox' && this.validateCheckbox())
20884 this.markInvalid();
20888 validateRadio : function()
20890 if(this.getVisibilityEl().hasClass('hidden')){
20894 if(this.allowBlank){
20900 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20901 if(!e.dom.checked){
20913 validateCheckbox : function()
20916 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20917 //return (this.getValue() == this.inputValue) ? true : false;
20920 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20928 for(var i in group){
20929 if(group[i].el.isVisible(true)){
20937 for(var i in group){
20942 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20949 * Mark this field as valid
20951 markValid : function()
20955 this.fireEvent('valid', this);
20957 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20960 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20967 if(this.inputType == 'radio'){
20968 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20969 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20970 e.findParent('.form-group', false, true).addClass(_this.validClass);
20977 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20978 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20982 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20988 for(var i in group){
20989 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20990 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20995 * Mark this field as invalid
20996 * @param {String} msg The validation message
20998 markInvalid : function(msg)
21000 if(this.allowBlank){
21006 this.fireEvent('invalid', this, msg);
21008 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21011 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21015 label.markInvalid();
21018 if(this.inputType == 'radio'){
21019 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21020 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21021 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21028 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21029 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21033 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21039 for(var i in group){
21040 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21041 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21046 clearInvalid : function()
21048 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21050 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21052 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21054 if (label && label.iconEl) {
21055 label.iconEl.removeClass(label.validClass);
21056 label.iconEl.removeClass(label.invalidClass);
21060 disable : function()
21062 if(this.inputType != 'radio'){
21063 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21070 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21071 _this.getActionEl().addClass(this.disabledClass);
21072 e.dom.disabled = true;
21076 this.disabled = true;
21077 this.fireEvent("disable", this);
21081 enable : function()
21083 if(this.inputType != 'radio'){
21084 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21091 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21092 _this.getActionEl().removeClass(this.disabledClass);
21093 e.dom.disabled = false;
21097 this.disabled = false;
21098 this.fireEvent("enable", this);
21102 setBoxLabel : function(v)
21107 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21113 Roo.apply(Roo.bootstrap.CheckBox, {
21118 * register a CheckBox Group
21119 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21121 register : function(checkbox)
21123 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21124 this.groups[checkbox.groupId] = {};
21127 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21131 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21135 * fetch a CheckBox Group based on the group ID
21136 * @param {string} the group ID
21137 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21139 get: function(groupId) {
21140 if (typeof(this.groups[groupId]) == 'undefined') {
21144 return this.groups[groupId] ;
21157 * @class Roo.bootstrap.Radio
21158 * @extends Roo.bootstrap.Component
21159 * Bootstrap Radio class
21160 * @cfg {String} boxLabel - the label associated
21161 * @cfg {String} value - the value of radio
21164 * Create a new Radio
21165 * @param {Object} config The config object
21167 Roo.bootstrap.Radio = function(config){
21168 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21172 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21178 getAutoCreate : function()
21182 cls : 'form-group radio',
21187 html : this.boxLabel
21195 initEvents : function()
21197 this.parent().register(this);
21199 this.el.on('click', this.onClick, this);
21203 onClick : function(e)
21205 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21206 this.setChecked(true);
21210 setChecked : function(state, suppressEvent)
21212 this.parent().setValue(this.value, suppressEvent);
21216 setBoxLabel : function(v)
21221 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21236 * @class Roo.bootstrap.SecurePass
21237 * @extends Roo.bootstrap.Input
21238 * Bootstrap SecurePass class
21242 * Create a new SecurePass
21243 * @param {Object} config The config object
21246 Roo.bootstrap.SecurePass = function (config) {
21247 // these go here, so the translation tool can replace them..
21249 PwdEmpty: "Please type a password, and then retype it to confirm.",
21250 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21251 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21252 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21253 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21254 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21255 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21256 TooWeak: "Your password is Too Weak."
21258 this.meterLabel = "Password strength:";
21259 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21260 this.meterClass = [
21261 "roo-password-meter-tooweak",
21262 "roo-password-meter-weak",
21263 "roo-password-meter-medium",
21264 "roo-password-meter-strong",
21265 "roo-password-meter-grey"
21270 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21273 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21275 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21277 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21278 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21279 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21280 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21281 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21282 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21283 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21293 * @cfg {String/Object} Label for the strength meter (defaults to
21294 * 'Password strength:')
21299 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21300 * ['Weak', 'Medium', 'Strong'])
21303 pwdStrengths: false,
21316 initEvents: function ()
21318 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21320 if (this.el.is('input[type=password]') && Roo.isSafari) {
21321 this.el.on('keydown', this.SafariOnKeyDown, this);
21324 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21327 onRender: function (ct, position)
21329 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21330 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21331 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21333 this.trigger.createChild({
21338 cls: 'roo-password-meter-grey col-xs-12',
21341 //width: this.meterWidth + 'px'
21345 cls: 'roo-password-meter-text'
21351 if (this.hideTrigger) {
21352 this.trigger.setDisplayed(false);
21354 this.setSize(this.width || '', this.height || '');
21357 onDestroy: function ()
21359 if (this.trigger) {
21360 this.trigger.removeAllListeners();
21361 this.trigger.remove();
21364 this.wrap.remove();
21366 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21369 checkStrength: function ()
21371 var pwd = this.inputEl().getValue();
21372 if (pwd == this._lastPwd) {
21377 if (this.ClientSideStrongPassword(pwd)) {
21379 } else if (this.ClientSideMediumPassword(pwd)) {
21381 } else if (this.ClientSideWeakPassword(pwd)) {
21387 Roo.log('strength1: ' + strength);
21389 //var pm = this.trigger.child('div/div/div').dom;
21390 var pm = this.trigger.child('div/div');
21391 pm.removeClass(this.meterClass);
21392 pm.addClass(this.meterClass[strength]);
21395 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21397 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21399 this._lastPwd = pwd;
21403 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21405 this._lastPwd = '';
21407 var pm = this.trigger.child('div/div');
21408 pm.removeClass(this.meterClass);
21409 pm.addClass('roo-password-meter-grey');
21412 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21415 this.inputEl().dom.type='password';
21418 validateValue: function (value)
21421 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21424 if (value.length == 0) {
21425 if (this.allowBlank) {
21426 this.clearInvalid();
21430 this.markInvalid(this.errors.PwdEmpty);
21431 this.errorMsg = this.errors.PwdEmpty;
21439 if ('[\x21-\x7e]*'.match(value)) {
21440 this.markInvalid(this.errors.PwdBadChar);
21441 this.errorMsg = this.errors.PwdBadChar;
21444 if (value.length < 6) {
21445 this.markInvalid(this.errors.PwdShort);
21446 this.errorMsg = this.errors.PwdShort;
21449 if (value.length > 16) {
21450 this.markInvalid(this.errors.PwdLong);
21451 this.errorMsg = this.errors.PwdLong;
21455 if (this.ClientSideStrongPassword(value)) {
21457 } else if (this.ClientSideMediumPassword(value)) {
21459 } else if (this.ClientSideWeakPassword(value)) {
21466 if (strength < 2) {
21467 //this.markInvalid(this.errors.TooWeak);
21468 this.errorMsg = this.errors.TooWeak;
21473 console.log('strength2: ' + strength);
21475 //var pm = this.trigger.child('div/div/div').dom;
21477 var pm = this.trigger.child('div/div');
21478 pm.removeClass(this.meterClass);
21479 pm.addClass(this.meterClass[strength]);
21481 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21483 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21485 this.errorMsg = '';
21489 CharacterSetChecks: function (type)
21492 this.fResult = false;
21495 isctype: function (character, type)
21498 case this.kCapitalLetter:
21499 if (character >= 'A' && character <= 'Z') {
21504 case this.kSmallLetter:
21505 if (character >= 'a' && character <= 'z') {
21511 if (character >= '0' && character <= '9') {
21516 case this.kPunctuation:
21517 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21528 IsLongEnough: function (pwd, size)
21530 return !(pwd == null || isNaN(size) || pwd.length < size);
21533 SpansEnoughCharacterSets: function (word, nb)
21535 if (!this.IsLongEnough(word, nb))
21540 var characterSetChecks = new Array(
21541 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21542 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21545 for (var index = 0; index < word.length; ++index) {
21546 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21547 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21548 characterSetChecks[nCharSet].fResult = true;
21555 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21556 if (characterSetChecks[nCharSet].fResult) {
21561 if (nCharSets < nb) {
21567 ClientSideStrongPassword: function (pwd)
21569 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21572 ClientSideMediumPassword: function (pwd)
21574 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21577 ClientSideWeakPassword: function (pwd)
21579 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21582 })//<script type="text/javascript">
21585 * Based Ext JS Library 1.1.1
21586 * Copyright(c) 2006-2007, Ext JS, LLC.
21592 * @class Roo.HtmlEditorCore
21593 * @extends Roo.Component
21594 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21596 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21599 Roo.HtmlEditorCore = function(config){
21602 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21607 * @event initialize
21608 * Fires when the editor is fully initialized (including the iframe)
21609 * @param {Roo.HtmlEditorCore} this
21614 * Fires when the editor is first receives the focus. Any insertion must wait
21615 * until after this event.
21616 * @param {Roo.HtmlEditorCore} this
21620 * @event beforesync
21621 * Fires before the textarea is updated with content from the editor iframe. Return false
21622 * to cancel the sync.
21623 * @param {Roo.HtmlEditorCore} this
21624 * @param {String} html
21628 * @event beforepush
21629 * Fires before the iframe editor is updated with content from the textarea. Return false
21630 * to cancel the push.
21631 * @param {Roo.HtmlEditorCore} this
21632 * @param {String} html
21637 * Fires when the textarea is updated with content from the editor iframe.
21638 * @param {Roo.HtmlEditorCore} this
21639 * @param {String} html
21644 * Fires when the iframe editor is updated with content from the textarea.
21645 * @param {Roo.HtmlEditorCore} this
21646 * @param {String} html
21651 * @event editorevent
21652 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21653 * @param {Roo.HtmlEditorCore} this
21659 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21661 // defaults : white / black...
21662 this.applyBlacklists();
21669 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21673 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21679 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21684 * @cfg {Number} height (in pixels)
21688 * @cfg {Number} width (in pixels)
21693 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21696 stylesheets: false,
21701 // private properties
21702 validationEvent : false,
21704 initialized : false,
21706 sourceEditMode : false,
21707 onFocus : Roo.emptyFn,
21709 hideMode:'offsets',
21713 // blacklist + whitelisted elements..
21720 * Protected method that will not generally be called directly. It
21721 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21722 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21724 getDocMarkup : function(){
21728 // inherit styels from page...??
21729 if (this.stylesheets === false) {
21731 Roo.get(document.head).select('style').each(function(node) {
21732 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21735 Roo.get(document.head).select('link').each(function(node) {
21736 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21739 } else if (!this.stylesheets.length) {
21741 st = '<style type="text/css">' +
21742 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21745 st = '<style type="text/css">' +
21750 st += '<style type="text/css">' +
21751 'IMG { cursor: pointer } ' +
21754 var cls = 'roo-htmleditor-body';
21756 if(this.bodyCls.length){
21757 cls += ' ' + this.bodyCls;
21760 return '<html><head>' + st +
21761 //<style type="text/css">' +
21762 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21764 ' </head><body class="' + cls + '"></body></html>';
21768 onRender : function(ct, position)
21771 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21772 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21775 this.el.dom.style.border = '0 none';
21776 this.el.dom.setAttribute('tabIndex', -1);
21777 this.el.addClass('x-hidden hide');
21781 if(Roo.isIE){ // fix IE 1px bogus margin
21782 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21786 this.frameId = Roo.id();
21790 var iframe = this.owner.wrap.createChild({
21792 cls: 'form-control', // bootstrap..
21794 name: this.frameId,
21795 frameBorder : 'no',
21796 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21801 this.iframe = iframe.dom;
21803 this.assignDocWin();
21805 this.doc.designMode = 'on';
21808 this.doc.write(this.getDocMarkup());
21812 var task = { // must defer to wait for browser to be ready
21814 //console.log("run task?" + this.doc.readyState);
21815 this.assignDocWin();
21816 if(this.doc.body || this.doc.readyState == 'complete'){
21818 this.doc.designMode="on";
21822 Roo.TaskMgr.stop(task);
21823 this.initEditor.defer(10, this);
21830 Roo.TaskMgr.start(task);
21835 onResize : function(w, h)
21837 Roo.log('resize: ' +w + ',' + h );
21838 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21842 if(typeof w == 'number'){
21844 this.iframe.style.width = w + 'px';
21846 if(typeof h == 'number'){
21848 this.iframe.style.height = h + 'px';
21850 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21857 * Toggles the editor between standard and source edit mode.
21858 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21860 toggleSourceEdit : function(sourceEditMode){
21862 this.sourceEditMode = sourceEditMode === true;
21864 if(this.sourceEditMode){
21866 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21869 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21870 //this.iframe.className = '';
21873 //this.setSize(this.owner.wrap.getSize());
21874 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21881 * Protected method that will not generally be called directly. If you need/want
21882 * custom HTML cleanup, this is the method you should override.
21883 * @param {String} html The HTML to be cleaned
21884 * return {String} The cleaned HTML
21886 cleanHtml : function(html){
21887 html = String(html);
21888 if(html.length > 5){
21889 if(Roo.isSafari){ // strip safari nonsense
21890 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21893 if(html == ' '){
21900 * HTML Editor -> Textarea
21901 * Protected method that will not generally be called directly. Syncs the contents
21902 * of the editor iframe with the textarea.
21904 syncValue : function(){
21905 if(this.initialized){
21906 var bd = (this.doc.body || this.doc.documentElement);
21907 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21908 var html = bd.innerHTML;
21910 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21911 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21913 html = '<div style="'+m[0]+'">' + html + '</div>';
21916 html = this.cleanHtml(html);
21917 // fix up the special chars.. normaly like back quotes in word...
21918 // however we do not want to do this with chinese..
21919 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21920 var cc = b.charCodeAt();
21922 (cc >= 0x4E00 && cc < 0xA000 ) ||
21923 (cc >= 0x3400 && cc < 0x4E00 ) ||
21924 (cc >= 0xf900 && cc < 0xfb00 )
21930 if(this.owner.fireEvent('beforesync', this, html) !== false){
21931 this.el.dom.value = html;
21932 this.owner.fireEvent('sync', this, html);
21938 * Protected method that will not generally be called directly. Pushes the value of the textarea
21939 * into the iframe editor.
21941 pushValue : function(){
21942 if(this.initialized){
21943 var v = this.el.dom.value.trim();
21945 // if(v.length < 1){
21949 if(this.owner.fireEvent('beforepush', this, v) !== false){
21950 var d = (this.doc.body || this.doc.documentElement);
21952 this.cleanUpPaste();
21953 this.el.dom.value = d.innerHTML;
21954 this.owner.fireEvent('push', this, v);
21960 deferFocus : function(){
21961 this.focus.defer(10, this);
21965 focus : function(){
21966 if(this.win && !this.sourceEditMode){
21973 assignDocWin: function()
21975 var iframe = this.iframe;
21978 this.doc = iframe.contentWindow.document;
21979 this.win = iframe.contentWindow;
21981 // if (!Roo.get(this.frameId)) {
21984 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21985 // this.win = Roo.get(this.frameId).dom.contentWindow;
21987 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21991 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21992 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21997 initEditor : function(){
21998 //console.log("INIT EDITOR");
21999 this.assignDocWin();
22003 this.doc.designMode="on";
22005 this.doc.write(this.getDocMarkup());
22008 var dbody = (this.doc.body || this.doc.documentElement);
22009 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22010 // this copies styles from the containing element into thsi one..
22011 // not sure why we need all of this..
22012 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22014 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22015 //ss['background-attachment'] = 'fixed'; // w3c
22016 dbody.bgProperties = 'fixed'; // ie
22017 //Roo.DomHelper.applyStyles(dbody, ss);
22018 Roo.EventManager.on(this.doc, {
22019 //'mousedown': this.onEditorEvent,
22020 'mouseup': this.onEditorEvent,
22021 'dblclick': this.onEditorEvent,
22022 'click': this.onEditorEvent,
22023 'keyup': this.onEditorEvent,
22028 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22030 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22031 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22033 this.initialized = true;
22035 this.owner.fireEvent('initialize', this);
22040 onDestroy : function(){
22046 //for (var i =0; i < this.toolbars.length;i++) {
22047 // // fixme - ask toolbars for heights?
22048 // this.toolbars[i].onDestroy();
22051 //this.wrap.dom.innerHTML = '';
22052 //this.wrap.remove();
22057 onFirstFocus : function(){
22059 this.assignDocWin();
22062 this.activated = true;
22065 if(Roo.isGecko){ // prevent silly gecko errors
22067 var s = this.win.getSelection();
22068 if(!s.focusNode || s.focusNode.nodeType != 3){
22069 var r = s.getRangeAt(0);
22070 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22075 this.execCmd('useCSS', true);
22076 this.execCmd('styleWithCSS', false);
22079 this.owner.fireEvent('activate', this);
22083 adjustFont: function(btn){
22084 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22085 //if(Roo.isSafari){ // safari
22088 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22089 if(Roo.isSafari){ // safari
22090 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22091 v = (v < 10) ? 10 : v;
22092 v = (v > 48) ? 48 : v;
22093 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22098 v = Math.max(1, v+adjust);
22100 this.execCmd('FontSize', v );
22103 onEditorEvent : function(e)
22105 this.owner.fireEvent('editorevent', this, e);
22106 // this.updateToolbar();
22107 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22110 insertTag : function(tg)
22112 // could be a bit smarter... -> wrap the current selected tRoo..
22113 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22115 range = this.createRange(this.getSelection());
22116 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22117 wrappingNode.appendChild(range.extractContents());
22118 range.insertNode(wrappingNode);
22125 this.execCmd("formatblock", tg);
22129 insertText : function(txt)
22133 var range = this.createRange();
22134 range.deleteContents();
22135 //alert(Sender.getAttribute('label'));
22137 range.insertNode(this.doc.createTextNode(txt));
22143 * Executes a Midas editor command on the editor document and performs necessary focus and
22144 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22145 * @param {String} cmd The Midas command
22146 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22148 relayCmd : function(cmd, value){
22150 this.execCmd(cmd, value);
22151 this.owner.fireEvent('editorevent', this);
22152 //this.updateToolbar();
22153 this.owner.deferFocus();
22157 * Executes a Midas editor command directly on the editor document.
22158 * For visual commands, you should use {@link #relayCmd} instead.
22159 * <b>This should only be called after the editor is initialized.</b>
22160 * @param {String} cmd The Midas command
22161 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22163 execCmd : function(cmd, value){
22164 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22171 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22173 * @param {String} text | dom node..
22175 insertAtCursor : function(text)
22178 if(!this.activated){
22184 var r = this.doc.selection.createRange();
22195 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22199 // from jquery ui (MIT licenced)
22201 var win = this.win;
22203 if (win.getSelection && win.getSelection().getRangeAt) {
22204 range = win.getSelection().getRangeAt(0);
22205 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22206 range.insertNode(node);
22207 } else if (win.document.selection && win.document.selection.createRange) {
22208 // no firefox support
22209 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22210 win.document.selection.createRange().pasteHTML(txt);
22212 // no firefox support
22213 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22214 this.execCmd('InsertHTML', txt);
22223 mozKeyPress : function(e){
22225 var c = e.getCharCode(), cmd;
22228 c = String.fromCharCode(c).toLowerCase();
22242 this.cleanUpPaste.defer(100, this);
22250 e.preventDefault();
22258 fixKeys : function(){ // load time branching for fastest keydown performance
22260 return function(e){
22261 var k = e.getKey(), r;
22264 r = this.doc.selection.createRange();
22267 r.pasteHTML('    ');
22274 r = this.doc.selection.createRange();
22276 var target = r.parentElement();
22277 if(!target || target.tagName.toLowerCase() != 'li'){
22279 r.pasteHTML('<br />');
22285 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22286 this.cleanUpPaste.defer(100, this);
22292 }else if(Roo.isOpera){
22293 return function(e){
22294 var k = e.getKey();
22298 this.execCmd('InsertHTML','    ');
22301 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22302 this.cleanUpPaste.defer(100, this);
22307 }else if(Roo.isSafari){
22308 return function(e){
22309 var k = e.getKey();
22313 this.execCmd('InsertText','\t');
22317 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22318 this.cleanUpPaste.defer(100, this);
22326 getAllAncestors: function()
22328 var p = this.getSelectedNode();
22331 a.push(p); // push blank onto stack..
22332 p = this.getParentElement();
22336 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22340 a.push(this.doc.body);
22344 lastSelNode : false,
22347 getSelection : function()
22349 this.assignDocWin();
22350 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22353 getSelectedNode: function()
22355 // this may only work on Gecko!!!
22357 // should we cache this!!!!
22362 var range = this.createRange(this.getSelection()).cloneRange();
22365 var parent = range.parentElement();
22367 var testRange = range.duplicate();
22368 testRange.moveToElementText(parent);
22369 if (testRange.inRange(range)) {
22372 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22375 parent = parent.parentElement;
22380 // is ancestor a text element.
22381 var ac = range.commonAncestorContainer;
22382 if (ac.nodeType == 3) {
22383 ac = ac.parentNode;
22386 var ar = ac.childNodes;
22389 var other_nodes = [];
22390 var has_other_nodes = false;
22391 for (var i=0;i<ar.length;i++) {
22392 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22395 // fullly contained node.
22397 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22402 // probably selected..
22403 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22404 other_nodes.push(ar[i]);
22408 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22413 has_other_nodes = true;
22415 if (!nodes.length && other_nodes.length) {
22416 nodes= other_nodes;
22418 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22424 createRange: function(sel)
22426 // this has strange effects when using with
22427 // top toolbar - not sure if it's a great idea.
22428 //this.editor.contentWindow.focus();
22429 if (typeof sel != "undefined") {
22431 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22433 return this.doc.createRange();
22436 return this.doc.createRange();
22439 getParentElement: function()
22442 this.assignDocWin();
22443 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22445 var range = this.createRange(sel);
22448 var p = range.commonAncestorContainer;
22449 while (p.nodeType == 3) { // text node
22460 * Range intersection.. the hard stuff...
22464 * [ -- selected range --- ]
22468 * if end is before start or hits it. fail.
22469 * if start is after end or hits it fail.
22471 * if either hits (but other is outside. - then it's not
22477 // @see http://www.thismuchiknow.co.uk/?p=64.
22478 rangeIntersectsNode : function(range, node)
22480 var nodeRange = node.ownerDocument.createRange();
22482 nodeRange.selectNode(node);
22484 nodeRange.selectNodeContents(node);
22487 var rangeStartRange = range.cloneRange();
22488 rangeStartRange.collapse(true);
22490 var rangeEndRange = range.cloneRange();
22491 rangeEndRange.collapse(false);
22493 var nodeStartRange = nodeRange.cloneRange();
22494 nodeStartRange.collapse(true);
22496 var nodeEndRange = nodeRange.cloneRange();
22497 nodeEndRange.collapse(false);
22499 return rangeStartRange.compareBoundaryPoints(
22500 Range.START_TO_START, nodeEndRange) == -1 &&
22501 rangeEndRange.compareBoundaryPoints(
22502 Range.START_TO_START, nodeStartRange) == 1;
22506 rangeCompareNode : function(range, node)
22508 var nodeRange = node.ownerDocument.createRange();
22510 nodeRange.selectNode(node);
22512 nodeRange.selectNodeContents(node);
22516 range.collapse(true);
22518 nodeRange.collapse(true);
22520 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22521 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22523 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22525 var nodeIsBefore = ss == 1;
22526 var nodeIsAfter = ee == -1;
22528 if (nodeIsBefore && nodeIsAfter) {
22531 if (!nodeIsBefore && nodeIsAfter) {
22532 return 1; //right trailed.
22535 if (nodeIsBefore && !nodeIsAfter) {
22536 return 2; // left trailed.
22542 // private? - in a new class?
22543 cleanUpPaste : function()
22545 // cleans up the whole document..
22546 Roo.log('cleanuppaste');
22548 this.cleanUpChildren(this.doc.body);
22549 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22550 if (clean != this.doc.body.innerHTML) {
22551 this.doc.body.innerHTML = clean;
22556 cleanWordChars : function(input) {// change the chars to hex code
22557 var he = Roo.HtmlEditorCore;
22559 var output = input;
22560 Roo.each(he.swapCodes, function(sw) {
22561 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22563 output = output.replace(swapper, sw[1]);
22570 cleanUpChildren : function (n)
22572 if (!n.childNodes.length) {
22575 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22576 this.cleanUpChild(n.childNodes[i]);
22583 cleanUpChild : function (node)
22586 //console.log(node);
22587 if (node.nodeName == "#text") {
22588 // clean up silly Windows -- stuff?
22591 if (node.nodeName == "#comment") {
22592 node.parentNode.removeChild(node);
22593 // clean up silly Windows -- stuff?
22596 var lcname = node.tagName.toLowerCase();
22597 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22598 // whitelist of tags..
22600 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22602 node.parentNode.removeChild(node);
22607 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22609 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22610 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22612 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22613 // remove_keep_children = true;
22616 if (remove_keep_children) {
22617 this.cleanUpChildren(node);
22618 // inserts everything just before this node...
22619 while (node.childNodes.length) {
22620 var cn = node.childNodes[0];
22621 node.removeChild(cn);
22622 node.parentNode.insertBefore(cn, node);
22624 node.parentNode.removeChild(node);
22628 if (!node.attributes || !node.attributes.length) {
22629 this.cleanUpChildren(node);
22633 function cleanAttr(n,v)
22636 if (v.match(/^\./) || v.match(/^\//)) {
22639 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22642 if (v.match(/^#/)) {
22645 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22646 node.removeAttribute(n);
22650 var cwhite = this.cwhite;
22651 var cblack = this.cblack;
22653 function cleanStyle(n,v)
22655 if (v.match(/expression/)) { //XSS?? should we even bother..
22656 node.removeAttribute(n);
22660 var parts = v.split(/;/);
22663 Roo.each(parts, function(p) {
22664 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22668 var l = p.split(':').shift().replace(/\s+/g,'');
22669 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22671 if ( cwhite.length && cblack.indexOf(l) > -1) {
22672 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22673 //node.removeAttribute(n);
22677 // only allow 'c whitelisted system attributes'
22678 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22679 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22680 //node.removeAttribute(n);
22690 if (clean.length) {
22691 node.setAttribute(n, clean.join(';'));
22693 node.removeAttribute(n);
22699 for (var i = node.attributes.length-1; i > -1 ; i--) {
22700 var a = node.attributes[i];
22703 if (a.name.toLowerCase().substr(0,2)=='on') {
22704 node.removeAttribute(a.name);
22707 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22708 node.removeAttribute(a.name);
22711 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22712 cleanAttr(a.name,a.value); // fixme..
22715 if (a.name == 'style') {
22716 cleanStyle(a.name,a.value);
22719 /// clean up MS crap..
22720 // tecnically this should be a list of valid class'es..
22723 if (a.name == 'class') {
22724 if (a.value.match(/^Mso/)) {
22725 node.className = '';
22728 if (a.value.match(/^body$/)) {
22729 node.className = '';
22740 this.cleanUpChildren(node);
22746 * Clean up MS wordisms...
22748 cleanWord : function(node)
22753 this.cleanWord(this.doc.body);
22756 if (node.nodeName == "#text") {
22757 // clean up silly Windows -- stuff?
22760 if (node.nodeName == "#comment") {
22761 node.parentNode.removeChild(node);
22762 // clean up silly Windows -- stuff?
22766 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22767 node.parentNode.removeChild(node);
22771 // remove - but keep children..
22772 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22773 while (node.childNodes.length) {
22774 var cn = node.childNodes[0];
22775 node.removeChild(cn);
22776 node.parentNode.insertBefore(cn, node);
22778 node.parentNode.removeChild(node);
22779 this.iterateChildren(node, this.cleanWord);
22783 if (node.className.length) {
22785 var cn = node.className.split(/\W+/);
22787 Roo.each(cn, function(cls) {
22788 if (cls.match(/Mso[a-zA-Z]+/)) {
22793 node.className = cna.length ? cna.join(' ') : '';
22795 node.removeAttribute("class");
22799 if (node.hasAttribute("lang")) {
22800 node.removeAttribute("lang");
22803 if (node.hasAttribute("style")) {
22805 var styles = node.getAttribute("style").split(";");
22807 Roo.each(styles, function(s) {
22808 if (!s.match(/:/)) {
22811 var kv = s.split(":");
22812 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22815 // what ever is left... we allow.
22818 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22819 if (!nstyle.length) {
22820 node.removeAttribute('style');
22823 this.iterateChildren(node, this.cleanWord);
22829 * iterateChildren of a Node, calling fn each time, using this as the scole..
22830 * @param {DomNode} node node to iterate children of.
22831 * @param {Function} fn method of this class to call on each item.
22833 iterateChildren : function(node, fn)
22835 if (!node.childNodes.length) {
22838 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22839 fn.call(this, node.childNodes[i])
22845 * cleanTableWidths.
22847 * Quite often pasting from word etc.. results in tables with column and widths.
22848 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22851 cleanTableWidths : function(node)
22856 this.cleanTableWidths(this.doc.body);
22861 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22864 Roo.log(node.tagName);
22865 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22866 this.iterateChildren(node, this.cleanTableWidths);
22869 if (node.hasAttribute('width')) {
22870 node.removeAttribute('width');
22874 if (node.hasAttribute("style")) {
22877 var styles = node.getAttribute("style").split(";");
22879 Roo.each(styles, function(s) {
22880 if (!s.match(/:/)) {
22883 var kv = s.split(":");
22884 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22887 // what ever is left... we allow.
22890 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22891 if (!nstyle.length) {
22892 node.removeAttribute('style');
22896 this.iterateChildren(node, this.cleanTableWidths);
22904 domToHTML : function(currentElement, depth, nopadtext) {
22906 depth = depth || 0;
22907 nopadtext = nopadtext || false;
22909 if (!currentElement) {
22910 return this.domToHTML(this.doc.body);
22913 //Roo.log(currentElement);
22915 var allText = false;
22916 var nodeName = currentElement.nodeName;
22917 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22919 if (nodeName == '#text') {
22921 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22926 if (nodeName != 'BODY') {
22929 // Prints the node tagName, such as <A>, <IMG>, etc
22932 for(i = 0; i < currentElement.attributes.length;i++) {
22934 var aname = currentElement.attributes.item(i).name;
22935 if (!currentElement.attributes.item(i).value.length) {
22938 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22941 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22950 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22953 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22958 // Traverse the tree
22960 var currentElementChild = currentElement.childNodes.item(i);
22961 var allText = true;
22962 var innerHTML = '';
22964 while (currentElementChild) {
22965 // Formatting code (indent the tree so it looks nice on the screen)
22966 var nopad = nopadtext;
22967 if (lastnode == 'SPAN') {
22971 if (currentElementChild.nodeName == '#text') {
22972 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22973 toadd = nopadtext ? toadd : toadd.trim();
22974 if (!nopad && toadd.length > 80) {
22975 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22977 innerHTML += toadd;
22980 currentElementChild = currentElement.childNodes.item(i);
22986 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22988 // Recursively traverse the tree structure of the child node
22989 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22990 lastnode = currentElementChild.nodeName;
22992 currentElementChild=currentElement.childNodes.item(i);
22998 // The remaining code is mostly for formatting the tree
22999 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23004 ret+= "</"+tagName+">";
23010 applyBlacklists : function()
23012 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23013 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23017 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23018 if (b.indexOf(tag) > -1) {
23021 this.white.push(tag);
23025 Roo.each(w, function(tag) {
23026 if (b.indexOf(tag) > -1) {
23029 if (this.white.indexOf(tag) > -1) {
23032 this.white.push(tag);
23037 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23038 if (w.indexOf(tag) > -1) {
23041 this.black.push(tag);
23045 Roo.each(b, function(tag) {
23046 if (w.indexOf(tag) > -1) {
23049 if (this.black.indexOf(tag) > -1) {
23052 this.black.push(tag);
23057 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23058 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23062 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23063 if (b.indexOf(tag) > -1) {
23066 this.cwhite.push(tag);
23070 Roo.each(w, function(tag) {
23071 if (b.indexOf(tag) > -1) {
23074 if (this.cwhite.indexOf(tag) > -1) {
23077 this.cwhite.push(tag);
23082 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23083 if (w.indexOf(tag) > -1) {
23086 this.cblack.push(tag);
23090 Roo.each(b, function(tag) {
23091 if (w.indexOf(tag) > -1) {
23094 if (this.cblack.indexOf(tag) > -1) {
23097 this.cblack.push(tag);
23102 setStylesheets : function(stylesheets)
23104 if(typeof(stylesheets) == 'string'){
23105 Roo.get(this.iframe.contentDocument.head).createChild({
23107 rel : 'stylesheet',
23116 Roo.each(stylesheets, function(s) {
23121 Roo.get(_this.iframe.contentDocument.head).createChild({
23123 rel : 'stylesheet',
23132 removeStylesheets : function()
23136 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23141 setStyle : function(style)
23143 Roo.get(this.iframe.contentDocument.head).createChild({
23152 // hide stuff that is not compatible
23166 * @event specialkey
23170 * @cfg {String} fieldClass @hide
23173 * @cfg {String} focusClass @hide
23176 * @cfg {String} autoCreate @hide
23179 * @cfg {String} inputType @hide
23182 * @cfg {String} invalidClass @hide
23185 * @cfg {String} invalidText @hide
23188 * @cfg {String} msgFx @hide
23191 * @cfg {String} validateOnBlur @hide
23195 Roo.HtmlEditorCore.white = [
23196 'area', 'br', 'img', 'input', 'hr', 'wbr',
23198 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23199 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23200 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23201 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23202 'table', 'ul', 'xmp',
23204 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23207 'dir', 'menu', 'ol', 'ul', 'dl',
23213 Roo.HtmlEditorCore.black = [
23214 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23216 'base', 'basefont', 'bgsound', 'blink', 'body',
23217 'frame', 'frameset', 'head', 'html', 'ilayer',
23218 'iframe', 'layer', 'link', 'meta', 'object',
23219 'script', 'style' ,'title', 'xml' // clean later..
23221 Roo.HtmlEditorCore.clean = [
23222 'script', 'style', 'title', 'xml'
23224 Roo.HtmlEditorCore.remove = [
23229 Roo.HtmlEditorCore.ablack = [
23233 Roo.HtmlEditorCore.aclean = [
23234 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23238 Roo.HtmlEditorCore.pwhite= [
23239 'http', 'https', 'mailto'
23242 // white listed style attributes.
23243 Roo.HtmlEditorCore.cwhite= [
23244 // 'text-align', /// default is to allow most things..
23250 // black listed style attributes.
23251 Roo.HtmlEditorCore.cblack= [
23252 // 'font-size' -- this can be set by the project
23256 Roo.HtmlEditorCore.swapCodes =[
23275 * @class Roo.bootstrap.HtmlEditor
23276 * @extends Roo.bootstrap.TextArea
23277 * Bootstrap HtmlEditor class
23280 * Create a new HtmlEditor
23281 * @param {Object} config The config object
23284 Roo.bootstrap.HtmlEditor = function(config){
23285 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23286 if (!this.toolbars) {
23287 this.toolbars = [];
23290 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23293 * @event initialize
23294 * Fires when the editor is fully initialized (including the iframe)
23295 * @param {HtmlEditor} this
23300 * Fires when the editor is first receives the focus. Any insertion must wait
23301 * until after this event.
23302 * @param {HtmlEditor} this
23306 * @event beforesync
23307 * Fires before the textarea is updated with content from the editor iframe. Return false
23308 * to cancel the sync.
23309 * @param {HtmlEditor} this
23310 * @param {String} html
23314 * @event beforepush
23315 * Fires before the iframe editor is updated with content from the textarea. Return false
23316 * to cancel the push.
23317 * @param {HtmlEditor} this
23318 * @param {String} html
23323 * Fires when the textarea is updated with content from the editor iframe.
23324 * @param {HtmlEditor} this
23325 * @param {String} html
23330 * Fires when the iframe editor is updated with content from the textarea.
23331 * @param {HtmlEditor} this
23332 * @param {String} html
23336 * @event editmodechange
23337 * Fires when the editor switches edit modes
23338 * @param {HtmlEditor} this
23339 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23341 editmodechange: true,
23343 * @event editorevent
23344 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23345 * @param {HtmlEditor} this
23349 * @event firstfocus
23350 * Fires when on first focus - needed by toolbars..
23351 * @param {HtmlEditor} this
23356 * Auto save the htmlEditor value as a file into Events
23357 * @param {HtmlEditor} this
23361 * @event savedpreview
23362 * preview the saved version of htmlEditor
23363 * @param {HtmlEditor} this
23370 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23374 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23379 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23384 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23389 * @cfg {Number} height (in pixels)
23393 * @cfg {Number} width (in pixels)
23398 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23401 stylesheets: false,
23406 // private properties
23407 validationEvent : false,
23409 initialized : false,
23412 onFocus : Roo.emptyFn,
23414 hideMode:'offsets',
23416 tbContainer : false,
23420 toolbarContainer :function() {
23421 return this.wrap.select('.x-html-editor-tb',true).first();
23425 * Protected method that will not generally be called directly. It
23426 * is called when the editor creates its toolbar. Override this method if you need to
23427 * add custom toolbar buttons.
23428 * @param {HtmlEditor} editor
23430 createToolbar : function(){
23431 Roo.log('renewing');
23432 Roo.log("create toolbars");
23434 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23435 this.toolbars[0].render(this.toolbarContainer());
23439 // if (!editor.toolbars || !editor.toolbars.length) {
23440 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23443 // for (var i =0 ; i < editor.toolbars.length;i++) {
23444 // editor.toolbars[i] = Roo.factory(
23445 // typeof(editor.toolbars[i]) == 'string' ?
23446 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23447 // Roo.bootstrap.HtmlEditor);
23448 // editor.toolbars[i].init(editor);
23454 onRender : function(ct, position)
23456 // Roo.log("Call onRender: " + this.xtype);
23458 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23460 this.wrap = this.inputEl().wrap({
23461 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23464 this.editorcore.onRender(ct, position);
23466 if (this.resizable) {
23467 this.resizeEl = new Roo.Resizable(this.wrap, {
23471 minHeight : this.height,
23472 height: this.height,
23473 handles : this.resizable,
23476 resize : function(r, w, h) {
23477 _t.onResize(w,h); // -something
23483 this.createToolbar(this);
23486 if(!this.width && this.resizable){
23487 this.setSize(this.wrap.getSize());
23489 if (this.resizeEl) {
23490 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23491 // should trigger onReize..
23497 onResize : function(w, h)
23499 Roo.log('resize: ' +w + ',' + h );
23500 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23504 if(this.inputEl() ){
23505 if(typeof w == 'number'){
23506 var aw = w - this.wrap.getFrameWidth('lr');
23507 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23510 if(typeof h == 'number'){
23511 var tbh = -11; // fixme it needs to tool bar size!
23512 for (var i =0; i < this.toolbars.length;i++) {
23513 // fixme - ask toolbars for heights?
23514 tbh += this.toolbars[i].el.getHeight();
23515 //if (this.toolbars[i].footer) {
23516 // tbh += this.toolbars[i].footer.el.getHeight();
23524 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23525 ah -= 5; // knock a few pixes off for look..
23526 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23530 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23531 this.editorcore.onResize(ew,eh);
23536 * Toggles the editor between standard and source edit mode.
23537 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23539 toggleSourceEdit : function(sourceEditMode)
23541 this.editorcore.toggleSourceEdit(sourceEditMode);
23543 if(this.editorcore.sourceEditMode){
23544 Roo.log('editor - showing textarea');
23547 // Roo.log(this.syncValue());
23549 this.inputEl().removeClass(['hide', 'x-hidden']);
23550 this.inputEl().dom.removeAttribute('tabIndex');
23551 this.inputEl().focus();
23553 Roo.log('editor - hiding textarea');
23555 // Roo.log(this.pushValue());
23558 this.inputEl().addClass(['hide', 'x-hidden']);
23559 this.inputEl().dom.setAttribute('tabIndex', -1);
23560 //this.deferFocus();
23563 if(this.resizable){
23564 this.setSize(this.wrap.getSize());
23567 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23570 // private (for BoxComponent)
23571 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23573 // private (for BoxComponent)
23574 getResizeEl : function(){
23578 // private (for BoxComponent)
23579 getPositionEl : function(){
23584 initEvents : function(){
23585 this.originalValue = this.getValue();
23589 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23592 // markInvalid : Roo.emptyFn,
23594 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23597 // clearInvalid : Roo.emptyFn,
23599 setValue : function(v){
23600 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23601 this.editorcore.pushValue();
23606 deferFocus : function(){
23607 this.focus.defer(10, this);
23611 focus : function(){
23612 this.editorcore.focus();
23618 onDestroy : function(){
23624 for (var i =0; i < this.toolbars.length;i++) {
23625 // fixme - ask toolbars for heights?
23626 this.toolbars[i].onDestroy();
23629 this.wrap.dom.innerHTML = '';
23630 this.wrap.remove();
23635 onFirstFocus : function(){
23636 //Roo.log("onFirstFocus");
23637 this.editorcore.onFirstFocus();
23638 for (var i =0; i < this.toolbars.length;i++) {
23639 this.toolbars[i].onFirstFocus();
23645 syncValue : function()
23647 this.editorcore.syncValue();
23650 pushValue : function()
23652 this.editorcore.pushValue();
23656 // hide stuff that is not compatible
23670 * @event specialkey
23674 * @cfg {String} fieldClass @hide
23677 * @cfg {String} focusClass @hide
23680 * @cfg {String} autoCreate @hide
23683 * @cfg {String} inputType @hide
23686 * @cfg {String} invalidClass @hide
23689 * @cfg {String} invalidText @hide
23692 * @cfg {String} msgFx @hide
23695 * @cfg {String} validateOnBlur @hide
23704 Roo.namespace('Roo.bootstrap.htmleditor');
23706 * @class Roo.bootstrap.HtmlEditorToolbar1
23711 new Roo.bootstrap.HtmlEditor({
23714 new Roo.bootstrap.HtmlEditorToolbar1({
23715 disable : { fonts: 1 , format: 1, ..., ... , ...],
23721 * @cfg {Object} disable List of elements to disable..
23722 * @cfg {Array} btns List of additional buttons.
23726 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23729 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23732 Roo.apply(this, config);
23734 // default disabled, based on 'good practice'..
23735 this.disable = this.disable || {};
23736 Roo.applyIf(this.disable, {
23739 specialElements : true
23741 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23743 this.editor = config.editor;
23744 this.editorcore = config.editor.editorcore;
23746 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23748 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23749 // dont call parent... till later.
23751 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23756 editorcore : false,
23761 "h1","h2","h3","h4","h5","h6",
23763 "abbr", "acronym", "address", "cite", "samp", "var",
23767 onRender : function(ct, position)
23769 // Roo.log("Call onRender: " + this.xtype);
23771 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23773 this.el.dom.style.marginBottom = '0';
23775 var editorcore = this.editorcore;
23776 var editor= this.editor;
23779 var btn = function(id,cmd , toggle, handler, html){
23781 var event = toggle ? 'toggle' : 'click';
23786 xns: Roo.bootstrap,
23789 enableToggle:toggle !== false,
23791 pressed : toggle ? false : null,
23794 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23795 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23801 // var cb_box = function...
23806 xns: Roo.bootstrap,
23807 glyphicon : 'font',
23811 xns: Roo.bootstrap,
23815 Roo.each(this.formats, function(f) {
23816 style.menu.items.push({
23818 xns: Roo.bootstrap,
23819 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23824 editorcore.insertTag(this.tagname);
23831 children.push(style);
23833 btn('bold',false,true);
23834 btn('italic',false,true);
23835 btn('align-left', 'justifyleft',true);
23836 btn('align-center', 'justifycenter',true);
23837 btn('align-right' , 'justifyright',true);
23838 btn('link', false, false, function(btn) {
23839 //Roo.log("create link?");
23840 var url = prompt(this.createLinkText, this.defaultLinkValue);
23841 if(url && url != 'http:/'+'/'){
23842 this.editorcore.relayCmd('createlink', url);
23845 btn('list','insertunorderedlist',true);
23846 btn('pencil', false,true, function(btn){
23848 this.toggleSourceEdit(btn.pressed);
23851 if (this.editor.btns.length > 0) {
23852 for (var i = 0; i<this.editor.btns.length; i++) {
23853 children.push(this.editor.btns[i]);
23861 xns: Roo.bootstrap,
23866 xns: Roo.bootstrap,
23871 cog.menu.items.push({
23873 xns: Roo.bootstrap,
23874 html : Clean styles,
23879 editorcore.insertTag(this.tagname);
23888 this.xtype = 'NavSimplebar';
23890 for(var i=0;i< children.length;i++) {
23892 this.buttons.add(this.addxtypeChild(children[i]));
23896 editor.on('editorevent', this.updateToolbar, this);
23898 onBtnClick : function(id)
23900 this.editorcore.relayCmd(id);
23901 this.editorcore.focus();
23905 * Protected method that will not generally be called directly. It triggers
23906 * a toolbar update by reading the markup state of the current selection in the editor.
23908 updateToolbar: function(){
23910 if(!this.editorcore.activated){
23911 this.editor.onFirstFocus(); // is this neeed?
23915 var btns = this.buttons;
23916 var doc = this.editorcore.doc;
23917 btns.get('bold').setActive(doc.queryCommandState('bold'));
23918 btns.get('italic').setActive(doc.queryCommandState('italic'));
23919 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23921 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23922 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23923 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23925 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23926 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23929 var ans = this.editorcore.getAllAncestors();
23930 if (this.formatCombo) {
23933 var store = this.formatCombo.store;
23934 this.formatCombo.setValue("");
23935 for (var i =0; i < ans.length;i++) {
23936 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23938 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23946 // hides menus... - so this cant be on a menu...
23947 Roo.bootstrap.MenuMgr.hideAll();
23949 Roo.bootstrap.MenuMgr.hideAll();
23950 //this.editorsyncValue();
23952 onFirstFocus: function() {
23953 this.buttons.each(function(item){
23957 toggleSourceEdit : function(sourceEditMode){
23960 if(sourceEditMode){
23961 Roo.log("disabling buttons");
23962 this.buttons.each( function(item){
23963 if(item.cmd != 'pencil'){
23969 Roo.log("enabling buttons");
23970 if(this.editorcore.initialized){
23971 this.buttons.each( function(item){
23977 Roo.log("calling toggole on editor");
23978 // tell the editor that it's been pressed..
23979 this.editor.toggleSourceEdit(sourceEditMode);
23989 * @class Roo.bootstrap.Table.AbstractSelectionModel
23990 * @extends Roo.util.Observable
23991 * Abstract base class for grid SelectionModels. It provides the interface that should be
23992 * implemented by descendant classes. This class should not be directly instantiated.
23995 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23996 this.locked = false;
23997 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24001 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24002 /** @ignore Called by the grid automatically. Do not call directly. */
24003 init : function(grid){
24009 * Locks the selections.
24012 this.locked = true;
24016 * Unlocks the selections.
24018 unlock : function(){
24019 this.locked = false;
24023 * Returns true if the selections are locked.
24024 * @return {Boolean}
24026 isLocked : function(){
24027 return this.locked;
24031 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24032 * @class Roo.bootstrap.Table.RowSelectionModel
24033 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24034 * It supports multiple selections and keyboard selection/navigation.
24036 * @param {Object} config
24039 Roo.bootstrap.Table.RowSelectionModel = function(config){
24040 Roo.apply(this, config);
24041 this.selections = new Roo.util.MixedCollection(false, function(o){
24046 this.lastActive = false;
24050 * @event selectionchange
24051 * Fires when the selection changes
24052 * @param {SelectionModel} this
24054 "selectionchange" : true,
24056 * @event afterselectionchange
24057 * Fires after the selection changes (eg. by key press or clicking)
24058 * @param {SelectionModel} this
24060 "afterselectionchange" : true,
24062 * @event beforerowselect
24063 * Fires when a row is selected being selected, return false to cancel.
24064 * @param {SelectionModel} this
24065 * @param {Number} rowIndex The selected index
24066 * @param {Boolean} keepExisting False if other selections will be cleared
24068 "beforerowselect" : true,
24071 * Fires when a row is selected.
24072 * @param {SelectionModel} this
24073 * @param {Number} rowIndex The selected index
24074 * @param {Roo.data.Record} r The record
24076 "rowselect" : true,
24078 * @event rowdeselect
24079 * Fires when a row is deselected.
24080 * @param {SelectionModel} this
24081 * @param {Number} rowIndex The selected index
24083 "rowdeselect" : true
24085 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24086 this.locked = false;
24089 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24091 * @cfg {Boolean} singleSelect
24092 * True to allow selection of only one row at a time (defaults to false)
24094 singleSelect : false,
24097 initEvents : function()
24100 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24101 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24102 //}else{ // allow click to work like normal
24103 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24105 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24106 this.grid.on("rowclick", this.handleMouseDown, this);
24108 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24109 "up" : function(e){
24111 this.selectPrevious(e.shiftKey);
24112 }else if(this.last !== false && this.lastActive !== false){
24113 var last = this.last;
24114 this.selectRange(this.last, this.lastActive-1);
24115 this.grid.getView().focusRow(this.lastActive);
24116 if(last !== false){
24120 this.selectFirstRow();
24122 this.fireEvent("afterselectionchange", this);
24124 "down" : function(e){
24126 this.selectNext(e.shiftKey);
24127 }else if(this.last !== false && this.lastActive !== false){
24128 var last = this.last;
24129 this.selectRange(this.last, this.lastActive+1);
24130 this.grid.getView().focusRow(this.lastActive);
24131 if(last !== false){
24135 this.selectFirstRow();
24137 this.fireEvent("afterselectionchange", this);
24141 this.grid.store.on('load', function(){
24142 this.selections.clear();
24145 var view = this.grid.view;
24146 view.on("refresh", this.onRefresh, this);
24147 view.on("rowupdated", this.onRowUpdated, this);
24148 view.on("rowremoved", this.onRemove, this);
24153 onRefresh : function()
24155 var ds = this.grid.store, i, v = this.grid.view;
24156 var s = this.selections;
24157 s.each(function(r){
24158 if((i = ds.indexOfId(r.id)) != -1){
24167 onRemove : function(v, index, r){
24168 this.selections.remove(r);
24172 onRowUpdated : function(v, index, r){
24173 if(this.isSelected(r)){
24174 v.onRowSelect(index);
24180 * @param {Array} records The records to select
24181 * @param {Boolean} keepExisting (optional) True to keep existing selections
24183 selectRecords : function(records, keepExisting)
24186 this.clearSelections();
24188 var ds = this.grid.store;
24189 for(var i = 0, len = records.length; i < len; i++){
24190 this.selectRow(ds.indexOf(records[i]), true);
24195 * Gets the number of selected rows.
24198 getCount : function(){
24199 return this.selections.length;
24203 * Selects the first row in the grid.
24205 selectFirstRow : function(){
24210 * Select the last row.
24211 * @param {Boolean} keepExisting (optional) True to keep existing selections
24213 selectLastRow : function(keepExisting){
24214 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24215 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24219 * Selects the row immediately following the last selected row.
24220 * @param {Boolean} keepExisting (optional) True to keep existing selections
24222 selectNext : function(keepExisting)
24224 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24225 this.selectRow(this.last+1, keepExisting);
24226 this.grid.getView().focusRow(this.last);
24231 * Selects the row that precedes the last selected row.
24232 * @param {Boolean} keepExisting (optional) True to keep existing selections
24234 selectPrevious : function(keepExisting){
24236 this.selectRow(this.last-1, keepExisting);
24237 this.grid.getView().focusRow(this.last);
24242 * Returns the selected records
24243 * @return {Array} Array of selected records
24245 getSelections : function(){
24246 return [].concat(this.selections.items);
24250 * Returns the first selected record.
24253 getSelected : function(){
24254 return this.selections.itemAt(0);
24259 * Clears all selections.
24261 clearSelections : function(fast)
24267 var ds = this.grid.store;
24268 var s = this.selections;
24269 s.each(function(r){
24270 this.deselectRow(ds.indexOfId(r.id));
24274 this.selections.clear();
24281 * Selects all rows.
24283 selectAll : function(){
24287 this.selections.clear();
24288 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24289 this.selectRow(i, true);
24294 * Returns True if there is a selection.
24295 * @return {Boolean}
24297 hasSelection : function(){
24298 return this.selections.length > 0;
24302 * Returns True if the specified row is selected.
24303 * @param {Number/Record} record The record or index of the record to check
24304 * @return {Boolean}
24306 isSelected : function(index){
24307 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24308 return (r && this.selections.key(r.id) ? true : false);
24312 * Returns True if the specified record id is selected.
24313 * @param {String} id The id of record to check
24314 * @return {Boolean}
24316 isIdSelected : function(id){
24317 return (this.selections.key(id) ? true : false);
24322 handleMouseDBClick : function(e, t){
24326 handleMouseDown : function(e, t)
24328 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24329 if(this.isLocked() || rowIndex < 0 ){
24332 if(e.shiftKey && this.last !== false){
24333 var last = this.last;
24334 this.selectRange(last, rowIndex, e.ctrlKey);
24335 this.last = last; // reset the last
24339 var isSelected = this.isSelected(rowIndex);
24340 //Roo.log("select row:" + rowIndex);
24342 this.deselectRow(rowIndex);
24344 this.selectRow(rowIndex, true);
24348 if(e.button !== 0 && isSelected){
24349 alert('rowIndex 2: ' + rowIndex);
24350 view.focusRow(rowIndex);
24351 }else if(e.ctrlKey && isSelected){
24352 this.deselectRow(rowIndex);
24353 }else if(!isSelected){
24354 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24355 view.focusRow(rowIndex);
24359 this.fireEvent("afterselectionchange", this);
24362 handleDragableRowClick : function(grid, rowIndex, e)
24364 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24365 this.selectRow(rowIndex, false);
24366 grid.view.focusRow(rowIndex);
24367 this.fireEvent("afterselectionchange", this);
24372 * Selects multiple rows.
24373 * @param {Array} rows Array of the indexes of the row to select
24374 * @param {Boolean} keepExisting (optional) True to keep existing selections
24376 selectRows : function(rows, keepExisting){
24378 this.clearSelections();
24380 for(var i = 0, len = rows.length; i < len; i++){
24381 this.selectRow(rows[i], true);
24386 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24387 * @param {Number} startRow The index of the first row in the range
24388 * @param {Number} endRow The index of the last row in the range
24389 * @param {Boolean} keepExisting (optional) True to retain existing selections
24391 selectRange : function(startRow, endRow, keepExisting){
24396 this.clearSelections();
24398 if(startRow <= endRow){
24399 for(var i = startRow; i <= endRow; i++){
24400 this.selectRow(i, true);
24403 for(var i = startRow; i >= endRow; i--){
24404 this.selectRow(i, true);
24410 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24411 * @param {Number} startRow The index of the first row in the range
24412 * @param {Number} endRow The index of the last row in the range
24414 deselectRange : function(startRow, endRow, preventViewNotify){
24418 for(var i = startRow; i <= endRow; i++){
24419 this.deselectRow(i, preventViewNotify);
24425 * @param {Number} row The index of the row to select
24426 * @param {Boolean} keepExisting (optional) True to keep existing selections
24428 selectRow : function(index, keepExisting, preventViewNotify)
24430 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24433 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24434 if(!keepExisting || this.singleSelect){
24435 this.clearSelections();
24438 var r = this.grid.store.getAt(index);
24439 //console.log('selectRow - record id :' + r.id);
24441 this.selections.add(r);
24442 this.last = this.lastActive = index;
24443 if(!preventViewNotify){
24444 var proxy = new Roo.Element(
24445 this.grid.getRowDom(index)
24447 proxy.addClass('bg-info info');
24449 this.fireEvent("rowselect", this, index, r);
24450 this.fireEvent("selectionchange", this);
24456 * @param {Number} row The index of the row to deselect
24458 deselectRow : function(index, preventViewNotify)
24463 if(this.last == index){
24466 if(this.lastActive == index){
24467 this.lastActive = false;
24470 var r = this.grid.store.getAt(index);
24475 this.selections.remove(r);
24476 //.console.log('deselectRow - record id :' + r.id);
24477 if(!preventViewNotify){
24479 var proxy = new Roo.Element(
24480 this.grid.getRowDom(index)
24482 proxy.removeClass('bg-info info');
24484 this.fireEvent("rowdeselect", this, index);
24485 this.fireEvent("selectionchange", this);
24489 restoreLast : function(){
24491 this.last = this._last;
24496 acceptsNav : function(row, col, cm){
24497 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24501 onEditorKey : function(field, e){
24502 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24507 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24509 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24511 }else if(k == e.ENTER && !e.ctrlKey){
24515 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24517 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24519 }else if(k == e.ESC){
24523 g.startEditing(newCell[0], newCell[1]);
24529 * Ext JS Library 1.1.1
24530 * Copyright(c) 2006-2007, Ext JS, LLC.
24532 * Originally Released Under LGPL - original licence link has changed is not relivant.
24535 * <script type="text/javascript">
24539 * @class Roo.bootstrap.PagingToolbar
24540 * @extends Roo.bootstrap.NavSimplebar
24541 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24543 * Create a new PagingToolbar
24544 * @param {Object} config The config object
24545 * @param {Roo.data.Store} store
24547 Roo.bootstrap.PagingToolbar = function(config)
24549 // old args format still supported... - xtype is prefered..
24550 // created from xtype...
24552 this.ds = config.dataSource;
24554 if (config.store && !this.ds) {
24555 this.store= Roo.factory(config.store, Roo.data);
24556 this.ds = this.store;
24557 this.ds.xmodule = this.xmodule || false;
24560 this.toolbarItems = [];
24561 if (config.items) {
24562 this.toolbarItems = config.items;
24565 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24570 this.bind(this.ds);
24573 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24577 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24579 * @cfg {Roo.data.Store} dataSource
24580 * The underlying data store providing the paged data
24583 * @cfg {String/HTMLElement/Element} container
24584 * container The id or element that will contain the toolbar
24587 * @cfg {Boolean} displayInfo
24588 * True to display the displayMsg (defaults to false)
24591 * @cfg {Number} pageSize
24592 * The number of records to display per page (defaults to 20)
24596 * @cfg {String} displayMsg
24597 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24599 displayMsg : 'Displaying {0} - {1} of {2}',
24601 * @cfg {String} emptyMsg
24602 * The message to display when no records are found (defaults to "No data to display")
24604 emptyMsg : 'No data to display',
24606 * Customizable piece of the default paging text (defaults to "Page")
24609 beforePageText : "Page",
24611 * Customizable piece of the default paging text (defaults to "of %0")
24614 afterPageText : "of {0}",
24616 * Customizable piece of the default paging text (defaults to "First Page")
24619 firstText : "First Page",
24621 * Customizable piece of the default paging text (defaults to "Previous Page")
24624 prevText : "Previous Page",
24626 * Customizable piece of the default paging text (defaults to "Next Page")
24629 nextText : "Next Page",
24631 * Customizable piece of the default paging text (defaults to "Last Page")
24634 lastText : "Last Page",
24636 * Customizable piece of the default paging text (defaults to "Refresh")
24639 refreshText : "Refresh",
24643 onRender : function(ct, position)
24645 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24646 this.navgroup.parentId = this.id;
24647 this.navgroup.onRender(this.el, null);
24648 // add the buttons to the navgroup
24650 if(this.displayInfo){
24651 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24652 this.displayEl = this.el.select('.x-paging-info', true).first();
24653 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24654 // this.displayEl = navel.el.select('span',true).first();
24660 Roo.each(_this.buttons, function(e){ // this might need to use render????
24661 Roo.factory(e).render(_this.el);
24665 Roo.each(_this.toolbarItems, function(e) {
24666 _this.navgroup.addItem(e);
24670 this.first = this.navgroup.addItem({
24671 tooltip: this.firstText,
24673 icon : 'fa fa-backward',
24675 preventDefault: true,
24676 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24679 this.prev = this.navgroup.addItem({
24680 tooltip: this.prevText,
24682 icon : 'fa fa-step-backward',
24684 preventDefault: true,
24685 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24687 //this.addSeparator();
24690 var field = this.navgroup.addItem( {
24692 cls : 'x-paging-position',
24694 html : this.beforePageText +
24695 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24696 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24699 this.field = field.el.select('input', true).first();
24700 this.field.on("keydown", this.onPagingKeydown, this);
24701 this.field.on("focus", function(){this.dom.select();});
24704 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24705 //this.field.setHeight(18);
24706 //this.addSeparator();
24707 this.next = this.navgroup.addItem({
24708 tooltip: this.nextText,
24710 html : ' <i class="fa fa-step-forward">',
24712 preventDefault: true,
24713 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24715 this.last = this.navgroup.addItem({
24716 tooltip: this.lastText,
24717 icon : 'fa fa-forward',
24720 preventDefault: true,
24721 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24723 //this.addSeparator();
24724 this.loading = this.navgroup.addItem({
24725 tooltip: this.refreshText,
24726 icon: 'fa fa-refresh',
24727 preventDefault: true,
24728 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24734 updateInfo : function(){
24735 if(this.displayEl){
24736 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24737 var msg = count == 0 ?
24741 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24743 this.displayEl.update(msg);
24748 onLoad : function(ds, r, o)
24750 this.cursor = o.params.start ? o.params.start : 0;
24752 var d = this.getPageData(),
24757 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24758 this.field.dom.value = ap;
24759 this.first.setDisabled(ap == 1);
24760 this.prev.setDisabled(ap == 1);
24761 this.next.setDisabled(ap == ps);
24762 this.last.setDisabled(ap == ps);
24763 this.loading.enable();
24768 getPageData : function(){
24769 var total = this.ds.getTotalCount();
24772 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24773 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24778 onLoadError : function(){
24779 this.loading.enable();
24783 onPagingKeydown : function(e){
24784 var k = e.getKey();
24785 var d = this.getPageData();
24787 var v = this.field.dom.value, pageNum;
24788 if(!v || isNaN(pageNum = parseInt(v, 10))){
24789 this.field.dom.value = d.activePage;
24792 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24793 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24796 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))
24798 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24799 this.field.dom.value = pageNum;
24800 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24803 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24805 var v = this.field.dom.value, pageNum;
24806 var increment = (e.shiftKey) ? 10 : 1;
24807 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24810 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24811 this.field.dom.value = d.activePage;
24814 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24816 this.field.dom.value = parseInt(v, 10) + increment;
24817 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24818 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24825 beforeLoad : function(){
24827 this.loading.disable();
24832 onClick : function(which){
24841 ds.load({params:{start: 0, limit: this.pageSize}});
24844 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24847 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24850 var total = ds.getTotalCount();
24851 var extra = total % this.pageSize;
24852 var lastStart = extra ? (total - extra) : total-this.pageSize;
24853 ds.load({params:{start: lastStart, limit: this.pageSize}});
24856 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24862 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24863 * @param {Roo.data.Store} store The data store to unbind
24865 unbind : function(ds){
24866 ds.un("beforeload", this.beforeLoad, this);
24867 ds.un("load", this.onLoad, this);
24868 ds.un("loadexception", this.onLoadError, this);
24869 ds.un("remove", this.updateInfo, this);
24870 ds.un("add", this.updateInfo, this);
24871 this.ds = undefined;
24875 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24876 * @param {Roo.data.Store} store The data store to bind
24878 bind : function(ds){
24879 ds.on("beforeload", this.beforeLoad, this);
24880 ds.on("load", this.onLoad, this);
24881 ds.on("loadexception", this.onLoadError, this);
24882 ds.on("remove", this.updateInfo, this);
24883 ds.on("add", this.updateInfo, this);
24894 * @class Roo.bootstrap.MessageBar
24895 * @extends Roo.bootstrap.Component
24896 * Bootstrap MessageBar class
24897 * @cfg {String} html contents of the MessageBar
24898 * @cfg {String} weight (info | success | warning | danger) default info
24899 * @cfg {String} beforeClass insert the bar before the given class
24900 * @cfg {Boolean} closable (true | false) default false
24901 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24904 * Create a new Element
24905 * @param {Object} config The config object
24908 Roo.bootstrap.MessageBar = function(config){
24909 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24912 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24918 beforeClass: 'bootstrap-sticky-wrap',
24920 getAutoCreate : function(){
24924 cls: 'alert alert-dismissable alert-' + this.weight,
24929 html: this.html || ''
24935 cfg.cls += ' alert-messages-fixed';
24949 onRender : function(ct, position)
24951 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24954 var cfg = Roo.apply({}, this.getAutoCreate());
24958 cfg.cls += ' ' + this.cls;
24961 cfg.style = this.style;
24963 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24965 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24968 this.el.select('>button.close').on('click', this.hide, this);
24974 if (!this.rendered) {
24980 this.fireEvent('show', this);
24986 if (!this.rendered) {
24992 this.fireEvent('hide', this);
24995 update : function()
24997 // var e = this.el.dom.firstChild;
24999 // if(this.closable){
25000 // e = e.nextSibling;
25003 // e.data = this.html || '';
25005 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25021 * @class Roo.bootstrap.Graph
25022 * @extends Roo.bootstrap.Component
25023 * Bootstrap Graph class
25027 @cfg {String} graphtype bar | vbar | pie
25028 @cfg {number} g_x coodinator | centre x (pie)
25029 @cfg {number} g_y coodinator | centre y (pie)
25030 @cfg {number} g_r radius (pie)
25031 @cfg {number} g_height height of the chart (respected by all elements in the set)
25032 @cfg {number} g_width width of the chart (respected by all elements in the set)
25033 @cfg {Object} title The title of the chart
25036 -opts (object) options for the chart
25038 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25039 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25041 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.
25042 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25044 o stretch (boolean)
25046 -opts (object) options for the pie
25049 o startAngle (number)
25050 o endAngle (number)
25054 * Create a new Input
25055 * @param {Object} config The config object
25058 Roo.bootstrap.Graph = function(config){
25059 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25065 * The img click event for the img.
25066 * @param {Roo.EventObject} e
25072 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25083 //g_colors: this.colors,
25090 getAutoCreate : function(){
25101 onRender : function(ct,position){
25104 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25106 if (typeof(Raphael) == 'undefined') {
25107 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25111 this.raphael = Raphael(this.el.dom);
25113 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25114 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25115 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25116 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25118 r.text(160, 10, "Single Series Chart").attr(txtattr);
25119 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25120 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25121 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25123 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25124 r.barchart(330, 10, 300, 220, data1);
25125 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25126 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25129 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25130 // r.barchart(30, 30, 560, 250, xdata, {
25131 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25132 // axis : "0 0 1 1",
25133 // axisxlabels : xdata
25134 // //yvalues : cols,
25137 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25139 // this.load(null,xdata,{
25140 // axis : "0 0 1 1",
25141 // axisxlabels : xdata
25146 load : function(graphtype,xdata,opts)
25148 this.raphael.clear();
25150 graphtype = this.graphtype;
25155 var r = this.raphael,
25156 fin = function () {
25157 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25159 fout = function () {
25160 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25162 pfin = function() {
25163 this.sector.stop();
25164 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25167 this.label[0].stop();
25168 this.label[0].attr({ r: 7.5 });
25169 this.label[1].attr({ "font-weight": 800 });
25172 pfout = function() {
25173 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25176 this.label[0].animate({ r: 5 }, 500, "bounce");
25177 this.label[1].attr({ "font-weight": 400 });
25183 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25186 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25189 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25190 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25192 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25199 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25204 setTitle: function(o)
25209 initEvents: function() {
25212 this.el.on('click', this.onClick, this);
25216 onClick : function(e)
25218 Roo.log('img onclick');
25219 this.fireEvent('click', this, e);
25231 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25234 * @class Roo.bootstrap.dash.NumberBox
25235 * @extends Roo.bootstrap.Component
25236 * Bootstrap NumberBox class
25237 * @cfg {String} headline Box headline
25238 * @cfg {String} content Box content
25239 * @cfg {String} icon Box icon
25240 * @cfg {String} footer Footer text
25241 * @cfg {String} fhref Footer href
25244 * Create a new NumberBox
25245 * @param {Object} config The config object
25249 Roo.bootstrap.dash.NumberBox = function(config){
25250 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25254 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25263 getAutoCreate : function(){
25267 cls : 'small-box ',
25275 cls : 'roo-headline',
25276 html : this.headline
25280 cls : 'roo-content',
25281 html : this.content
25295 cls : 'ion ' + this.icon
25304 cls : 'small-box-footer',
25305 href : this.fhref || '#',
25309 cfg.cn.push(footer);
25316 onRender : function(ct,position){
25317 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25324 setHeadline: function (value)
25326 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25329 setFooter: function (value, href)
25331 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25334 this.el.select('a.small-box-footer',true).first().attr('href', href);
25339 setContent: function (value)
25341 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25344 initEvents: function()
25358 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25361 * @class Roo.bootstrap.dash.TabBox
25362 * @extends Roo.bootstrap.Component
25363 * Bootstrap TabBox class
25364 * @cfg {String} title Title of the TabBox
25365 * @cfg {String} icon Icon of the TabBox
25366 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25367 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25370 * Create a new TabBox
25371 * @param {Object} config The config object
25375 Roo.bootstrap.dash.TabBox = function(config){
25376 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25381 * When a pane is added
25382 * @param {Roo.bootstrap.dash.TabPane} pane
25386 * @event activatepane
25387 * When a pane is activated
25388 * @param {Roo.bootstrap.dash.TabPane} pane
25390 "activatepane" : true
25398 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25403 tabScrollable : false,
25405 getChildContainer : function()
25407 return this.el.select('.tab-content', true).first();
25410 getAutoCreate : function(){
25414 cls: 'pull-left header',
25422 cls: 'fa ' + this.icon
25428 cls: 'nav nav-tabs pull-right',
25434 if(this.tabScrollable){
25441 cls: 'nav nav-tabs pull-right',
25452 cls: 'nav-tabs-custom',
25457 cls: 'tab-content no-padding',
25465 initEvents : function()
25467 //Roo.log('add add pane handler');
25468 this.on('addpane', this.onAddPane, this);
25471 * Updates the box title
25472 * @param {String} html to set the title to.
25474 setTitle : function(value)
25476 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25478 onAddPane : function(pane)
25480 this.panes.push(pane);
25481 //Roo.log('addpane');
25483 // tabs are rendere left to right..
25484 if(!this.showtabs){
25488 var ctr = this.el.select('.nav-tabs', true).first();
25491 var existing = ctr.select('.nav-tab',true);
25492 var qty = existing.getCount();;
25495 var tab = ctr.createChild({
25497 cls : 'nav-tab' + (qty ? '' : ' active'),
25505 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25508 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25510 pane.el.addClass('active');
25515 onTabClick : function(ev,un,ob,pane)
25517 //Roo.log('tab - prev default');
25518 ev.preventDefault();
25521 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25522 pane.tab.addClass('active');
25523 //Roo.log(pane.title);
25524 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25525 // technically we should have a deactivate event.. but maybe add later.
25526 // and it should not de-activate the selected tab...
25527 this.fireEvent('activatepane', pane);
25528 pane.el.addClass('active');
25529 pane.fireEvent('activate');
25534 getActivePane : function()
25537 Roo.each(this.panes, function(p) {
25538 if(p.el.hasClass('active')){
25559 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25561 * @class Roo.bootstrap.TabPane
25562 * @extends Roo.bootstrap.Component
25563 * Bootstrap TabPane class
25564 * @cfg {Boolean} active (false | true) Default false
25565 * @cfg {String} title title of panel
25569 * Create a new TabPane
25570 * @param {Object} config The config object
25573 Roo.bootstrap.dash.TabPane = function(config){
25574 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25580 * When a pane is activated
25581 * @param {Roo.bootstrap.dash.TabPane} pane
25588 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25593 // the tabBox that this is attached to.
25596 getAutoCreate : function()
25604 cfg.cls += ' active';
25609 initEvents : function()
25611 //Roo.log('trigger add pane handler');
25612 this.parent().fireEvent('addpane', this)
25616 * Updates the tab title
25617 * @param {String} html to set the title to.
25619 setTitle: function(str)
25625 this.tab.select('a', true).first().dom.innerHTML = str;
25642 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25645 * @class Roo.bootstrap.menu.Menu
25646 * @extends Roo.bootstrap.Component
25647 * Bootstrap Menu class - container for Menu
25648 * @cfg {String} html Text of the menu
25649 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25650 * @cfg {String} icon Font awesome icon
25651 * @cfg {String} pos Menu align to (top | bottom) default bottom
25655 * Create a new Menu
25656 * @param {Object} config The config object
25660 Roo.bootstrap.menu.Menu = function(config){
25661 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25665 * @event beforeshow
25666 * Fires before this menu is displayed
25667 * @param {Roo.bootstrap.menu.Menu} this
25671 * @event beforehide
25672 * Fires before this menu is hidden
25673 * @param {Roo.bootstrap.menu.Menu} this
25678 * Fires after this menu is displayed
25679 * @param {Roo.bootstrap.menu.Menu} this
25684 * Fires after this menu is hidden
25685 * @param {Roo.bootstrap.menu.Menu} this
25690 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25691 * @param {Roo.bootstrap.menu.Menu} this
25692 * @param {Roo.EventObject} e
25699 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25703 weight : 'default',
25708 getChildContainer : function() {
25709 if(this.isSubMenu){
25713 return this.el.select('ul.dropdown-menu', true).first();
25716 getAutoCreate : function()
25721 cls : 'roo-menu-text',
25729 cls : 'fa ' + this.icon
25740 cls : 'dropdown-button btn btn-' + this.weight,
25745 cls : 'dropdown-toggle btn btn-' + this.weight,
25755 cls : 'dropdown-menu'
25761 if(this.pos == 'top'){
25762 cfg.cls += ' dropup';
25765 if(this.isSubMenu){
25768 cls : 'dropdown-menu'
25775 onRender : function(ct, position)
25777 this.isSubMenu = ct.hasClass('dropdown-submenu');
25779 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25782 initEvents : function()
25784 if(this.isSubMenu){
25788 this.hidden = true;
25790 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25791 this.triggerEl.on('click', this.onTriggerPress, this);
25793 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25794 this.buttonEl.on('click', this.onClick, this);
25800 if(this.isSubMenu){
25804 return this.el.select('ul.dropdown-menu', true).first();
25807 onClick : function(e)
25809 this.fireEvent("click", this, e);
25812 onTriggerPress : function(e)
25814 if (this.isVisible()) {
25821 isVisible : function(){
25822 return !this.hidden;
25827 this.fireEvent("beforeshow", this);
25829 this.hidden = false;
25830 this.el.addClass('open');
25832 Roo.get(document).on("mouseup", this.onMouseUp, this);
25834 this.fireEvent("show", this);
25841 this.fireEvent("beforehide", this);
25843 this.hidden = true;
25844 this.el.removeClass('open');
25846 Roo.get(document).un("mouseup", this.onMouseUp);
25848 this.fireEvent("hide", this);
25851 onMouseUp : function()
25865 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25868 * @class Roo.bootstrap.menu.Item
25869 * @extends Roo.bootstrap.Component
25870 * Bootstrap MenuItem class
25871 * @cfg {Boolean} submenu (true | false) default false
25872 * @cfg {String} html text of the item
25873 * @cfg {String} href the link
25874 * @cfg {Boolean} disable (true | false) default false
25875 * @cfg {Boolean} preventDefault (true | false) default true
25876 * @cfg {String} icon Font awesome icon
25877 * @cfg {String} pos Submenu align to (left | right) default right
25881 * Create a new Item
25882 * @param {Object} config The config object
25886 Roo.bootstrap.menu.Item = function(config){
25887 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25891 * Fires when the mouse is hovering over this menu
25892 * @param {Roo.bootstrap.menu.Item} this
25893 * @param {Roo.EventObject} e
25898 * Fires when the mouse exits this menu
25899 * @param {Roo.bootstrap.menu.Item} this
25900 * @param {Roo.EventObject} e
25906 * The raw click event for the entire grid.
25907 * @param {Roo.EventObject} e
25913 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25918 preventDefault: true,
25923 getAutoCreate : function()
25928 cls : 'roo-menu-item-text',
25936 cls : 'fa ' + this.icon
25945 href : this.href || '#',
25952 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25956 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25958 if(this.pos == 'left'){
25959 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25966 initEvents : function()
25968 this.el.on('mouseover', this.onMouseOver, this);
25969 this.el.on('mouseout', this.onMouseOut, this);
25971 this.el.select('a', true).first().on('click', this.onClick, this);
25975 onClick : function(e)
25977 if(this.preventDefault){
25978 e.preventDefault();
25981 this.fireEvent("click", this, e);
25984 onMouseOver : function(e)
25986 if(this.submenu && this.pos == 'left'){
25987 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25990 this.fireEvent("mouseover", this, e);
25993 onMouseOut : function(e)
25995 this.fireEvent("mouseout", this, e);
26007 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26010 * @class Roo.bootstrap.menu.Separator
26011 * @extends Roo.bootstrap.Component
26012 * Bootstrap Separator class
26015 * Create a new Separator
26016 * @param {Object} config The config object
26020 Roo.bootstrap.menu.Separator = function(config){
26021 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26024 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26026 getAutoCreate : function(){
26047 * @class Roo.bootstrap.Tooltip
26048 * Bootstrap Tooltip class
26049 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26050 * to determine which dom element triggers the tooltip.
26052 * It needs to add support for additional attributes like tooltip-position
26055 * Create a new Toolti
26056 * @param {Object} config The config object
26059 Roo.bootstrap.Tooltip = function(config){
26060 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26062 this.alignment = Roo.bootstrap.Tooltip.alignment;
26064 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26065 this.alignment = config.alignment;
26070 Roo.apply(Roo.bootstrap.Tooltip, {
26072 * @function init initialize tooltip monitoring.
26076 currentTip : false,
26077 currentRegion : false,
26083 Roo.get(document).on('mouseover', this.enter ,this);
26084 Roo.get(document).on('mouseout', this.leave, this);
26087 this.currentTip = new Roo.bootstrap.Tooltip();
26090 enter : function(ev)
26092 var dom = ev.getTarget();
26094 //Roo.log(['enter',dom]);
26095 var el = Roo.fly(dom);
26096 if (this.currentEl) {
26098 //Roo.log(this.currentEl);
26099 //Roo.log(this.currentEl.contains(dom));
26100 if (this.currentEl == el) {
26103 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26109 if (this.currentTip.el) {
26110 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26114 if(!el || el.dom == document){
26120 // you can not look for children, as if el is the body.. then everythign is the child..
26121 if (!el.attr('tooltip')) { //
26122 if (!el.select("[tooltip]").elements.length) {
26125 // is the mouse over this child...?
26126 bindEl = el.select("[tooltip]").first();
26127 var xy = ev.getXY();
26128 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26129 //Roo.log("not in region.");
26132 //Roo.log("child element over..");
26135 this.currentEl = bindEl;
26136 this.currentTip.bind(bindEl);
26137 this.currentRegion = Roo.lib.Region.getRegion(dom);
26138 this.currentTip.enter();
26141 leave : function(ev)
26143 var dom = ev.getTarget();
26144 //Roo.log(['leave',dom]);
26145 if (!this.currentEl) {
26150 if (dom != this.currentEl.dom) {
26153 var xy = ev.getXY();
26154 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26157 // only activate leave if mouse cursor is outside... bounding box..
26162 if (this.currentTip) {
26163 this.currentTip.leave();
26165 //Roo.log('clear currentEl');
26166 this.currentEl = false;
26171 'left' : ['r-l', [-2,0], 'right'],
26172 'right' : ['l-r', [2,0], 'left'],
26173 'bottom' : ['t-b', [0,2], 'top'],
26174 'top' : [ 'b-t', [0,-2], 'bottom']
26180 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26185 delay : null, // can be { show : 300 , hide: 500}
26189 hoverState : null, //???
26191 placement : 'bottom',
26195 getAutoCreate : function(){
26202 cls : 'tooltip-arrow'
26205 cls : 'tooltip-inner'
26212 bind : function(el)
26218 enter : function () {
26220 if (this.timeout != null) {
26221 clearTimeout(this.timeout);
26224 this.hoverState = 'in';
26225 //Roo.log("enter - show");
26226 if (!this.delay || !this.delay.show) {
26231 this.timeout = setTimeout(function () {
26232 if (_t.hoverState == 'in') {
26235 }, this.delay.show);
26239 clearTimeout(this.timeout);
26241 this.hoverState = 'out';
26242 if (!this.delay || !this.delay.hide) {
26248 this.timeout = setTimeout(function () {
26249 //Roo.log("leave - timeout");
26251 if (_t.hoverState == 'out') {
26253 Roo.bootstrap.Tooltip.currentEl = false;
26258 show : function (msg)
26261 this.render(document.body);
26264 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26266 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26268 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26270 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26272 var placement = typeof this.placement == 'function' ?
26273 this.placement.call(this, this.el, on_el) :
26276 var autoToken = /\s?auto?\s?/i;
26277 var autoPlace = autoToken.test(placement);
26279 placement = placement.replace(autoToken, '') || 'top';
26283 //this.el.setXY([0,0]);
26285 //this.el.dom.style.display='block';
26287 //this.el.appendTo(on_el);
26289 var p = this.getPosition();
26290 var box = this.el.getBox();
26296 var align = this.alignment[placement];
26298 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26300 if(placement == 'top' || placement == 'bottom'){
26302 placement = 'right';
26305 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26306 placement = 'left';
26309 var scroll = Roo.select('body', true).first().getScroll();
26311 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26315 align = this.alignment[placement];
26318 this.el.alignTo(this.bindEl, align[0],align[1]);
26319 //var arrow = this.el.select('.arrow',true).first();
26320 //arrow.set(align[2],
26322 this.el.addClass(placement);
26324 this.el.addClass('in fade');
26326 this.hoverState = null;
26328 if (this.el.hasClass('fade')) {
26339 //this.el.setXY([0,0]);
26340 this.el.removeClass('in');
26356 * @class Roo.bootstrap.LocationPicker
26357 * @extends Roo.bootstrap.Component
26358 * Bootstrap LocationPicker class
26359 * @cfg {Number} latitude Position when init default 0
26360 * @cfg {Number} longitude Position when init default 0
26361 * @cfg {Number} zoom default 15
26362 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26363 * @cfg {Boolean} mapTypeControl default false
26364 * @cfg {Boolean} disableDoubleClickZoom default false
26365 * @cfg {Boolean} scrollwheel default true
26366 * @cfg {Boolean} streetViewControl default false
26367 * @cfg {Number} radius default 0
26368 * @cfg {String} locationName
26369 * @cfg {Boolean} draggable default true
26370 * @cfg {Boolean} enableAutocomplete default false
26371 * @cfg {Boolean} enableReverseGeocode default true
26372 * @cfg {String} markerTitle
26375 * Create a new LocationPicker
26376 * @param {Object} config The config object
26380 Roo.bootstrap.LocationPicker = function(config){
26382 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26387 * Fires when the picker initialized.
26388 * @param {Roo.bootstrap.LocationPicker} this
26389 * @param {Google Location} location
26393 * @event positionchanged
26394 * Fires when the picker position changed.
26395 * @param {Roo.bootstrap.LocationPicker} this
26396 * @param {Google Location} location
26398 positionchanged : true,
26401 * Fires when the map resize.
26402 * @param {Roo.bootstrap.LocationPicker} this
26407 * Fires when the map show.
26408 * @param {Roo.bootstrap.LocationPicker} this
26413 * Fires when the map hide.
26414 * @param {Roo.bootstrap.LocationPicker} this
26419 * Fires when click the map.
26420 * @param {Roo.bootstrap.LocationPicker} this
26421 * @param {Map event} e
26425 * @event mapRightClick
26426 * Fires when right click the map.
26427 * @param {Roo.bootstrap.LocationPicker} this
26428 * @param {Map event} e
26430 mapRightClick : true,
26432 * @event markerClick
26433 * Fires when click the marker.
26434 * @param {Roo.bootstrap.LocationPicker} this
26435 * @param {Map event} e
26437 markerClick : true,
26439 * @event markerRightClick
26440 * Fires when right click the marker.
26441 * @param {Roo.bootstrap.LocationPicker} this
26442 * @param {Map event} e
26444 markerRightClick : true,
26446 * @event OverlayViewDraw
26447 * Fires when OverlayView Draw
26448 * @param {Roo.bootstrap.LocationPicker} this
26450 OverlayViewDraw : true,
26452 * @event OverlayViewOnAdd
26453 * Fires when OverlayView Draw
26454 * @param {Roo.bootstrap.LocationPicker} this
26456 OverlayViewOnAdd : true,
26458 * @event OverlayViewOnRemove
26459 * Fires when OverlayView Draw
26460 * @param {Roo.bootstrap.LocationPicker} this
26462 OverlayViewOnRemove : true,
26464 * @event OverlayViewShow
26465 * Fires when OverlayView Draw
26466 * @param {Roo.bootstrap.LocationPicker} this
26467 * @param {Pixel} cpx
26469 OverlayViewShow : true,
26471 * @event OverlayViewHide
26472 * Fires when OverlayView Draw
26473 * @param {Roo.bootstrap.LocationPicker} this
26475 OverlayViewHide : true,
26477 * @event loadexception
26478 * Fires when load google lib failed.
26479 * @param {Roo.bootstrap.LocationPicker} this
26481 loadexception : true
26486 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26488 gMapContext: false,
26494 mapTypeControl: false,
26495 disableDoubleClickZoom: false,
26497 streetViewControl: false,
26501 enableAutocomplete: false,
26502 enableReverseGeocode: true,
26505 getAutoCreate: function()
26510 cls: 'roo-location-picker'
26516 initEvents: function(ct, position)
26518 if(!this.el.getWidth() || this.isApplied()){
26522 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26527 initial: function()
26529 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26530 this.fireEvent('loadexception', this);
26534 if(!this.mapTypeId){
26535 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26538 this.gMapContext = this.GMapContext();
26540 this.initOverlayView();
26542 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26546 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26547 _this.setPosition(_this.gMapContext.marker.position);
26550 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26551 _this.fireEvent('mapClick', this, event);
26555 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26556 _this.fireEvent('mapRightClick', this, event);
26560 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26561 _this.fireEvent('markerClick', this, event);
26565 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26566 _this.fireEvent('markerRightClick', this, event);
26570 this.setPosition(this.gMapContext.location);
26572 this.fireEvent('initial', this, this.gMapContext.location);
26575 initOverlayView: function()
26579 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26583 _this.fireEvent('OverlayViewDraw', _this);
26588 _this.fireEvent('OverlayViewOnAdd', _this);
26591 onRemove: function()
26593 _this.fireEvent('OverlayViewOnRemove', _this);
26596 show: function(cpx)
26598 _this.fireEvent('OverlayViewShow', _this, cpx);
26603 _this.fireEvent('OverlayViewHide', _this);
26609 fromLatLngToContainerPixel: function(event)
26611 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26614 isApplied: function()
26616 return this.getGmapContext() == false ? false : true;
26619 getGmapContext: function()
26621 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26624 GMapContext: function()
26626 var position = new google.maps.LatLng(this.latitude, this.longitude);
26628 var _map = new google.maps.Map(this.el.dom, {
26631 mapTypeId: this.mapTypeId,
26632 mapTypeControl: this.mapTypeControl,
26633 disableDoubleClickZoom: this.disableDoubleClickZoom,
26634 scrollwheel: this.scrollwheel,
26635 streetViewControl: this.streetViewControl,
26636 locationName: this.locationName,
26637 draggable: this.draggable,
26638 enableAutocomplete: this.enableAutocomplete,
26639 enableReverseGeocode: this.enableReverseGeocode
26642 var _marker = new google.maps.Marker({
26643 position: position,
26645 title: this.markerTitle,
26646 draggable: this.draggable
26653 location: position,
26654 radius: this.radius,
26655 locationName: this.locationName,
26656 addressComponents: {
26657 formatted_address: null,
26658 addressLine1: null,
26659 addressLine2: null,
26661 streetNumber: null,
26665 stateOrProvince: null
26668 domContainer: this.el.dom,
26669 geodecoder: new google.maps.Geocoder()
26673 drawCircle: function(center, radius, options)
26675 if (this.gMapContext.circle != null) {
26676 this.gMapContext.circle.setMap(null);
26680 options = Roo.apply({}, options, {
26681 strokeColor: "#0000FF",
26682 strokeOpacity: .35,
26684 fillColor: "#0000FF",
26688 options.map = this.gMapContext.map;
26689 options.radius = radius;
26690 options.center = center;
26691 this.gMapContext.circle = new google.maps.Circle(options);
26692 return this.gMapContext.circle;
26698 setPosition: function(location)
26700 this.gMapContext.location = location;
26701 this.gMapContext.marker.setPosition(location);
26702 this.gMapContext.map.panTo(location);
26703 this.drawCircle(location, this.gMapContext.radius, {});
26707 if (this.gMapContext.settings.enableReverseGeocode) {
26708 this.gMapContext.geodecoder.geocode({
26709 latLng: this.gMapContext.location
26710 }, function(results, status) {
26712 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26713 _this.gMapContext.locationName = results[0].formatted_address;
26714 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26716 _this.fireEvent('positionchanged', this, location);
26723 this.fireEvent('positionchanged', this, location);
26728 google.maps.event.trigger(this.gMapContext.map, "resize");
26730 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26732 this.fireEvent('resize', this);
26735 setPositionByLatLng: function(latitude, longitude)
26737 this.setPosition(new google.maps.LatLng(latitude, longitude));
26740 getCurrentPosition: function()
26743 latitude: this.gMapContext.location.lat(),
26744 longitude: this.gMapContext.location.lng()
26748 getAddressName: function()
26750 return this.gMapContext.locationName;
26753 getAddressComponents: function()
26755 return this.gMapContext.addressComponents;
26758 address_component_from_google_geocode: function(address_components)
26762 for (var i = 0; i < address_components.length; i++) {
26763 var component = address_components[i];
26764 if (component.types.indexOf("postal_code") >= 0) {
26765 result.postalCode = component.short_name;
26766 } else if (component.types.indexOf("street_number") >= 0) {
26767 result.streetNumber = component.short_name;
26768 } else if (component.types.indexOf("route") >= 0) {
26769 result.streetName = component.short_name;
26770 } else if (component.types.indexOf("neighborhood") >= 0) {
26771 result.city = component.short_name;
26772 } else if (component.types.indexOf("locality") >= 0) {
26773 result.city = component.short_name;
26774 } else if (component.types.indexOf("sublocality") >= 0) {
26775 result.district = component.short_name;
26776 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26777 result.stateOrProvince = component.short_name;
26778 } else if (component.types.indexOf("country") >= 0) {
26779 result.country = component.short_name;
26783 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26784 result.addressLine2 = "";
26788 setZoomLevel: function(zoom)
26790 this.gMapContext.map.setZoom(zoom);
26803 this.fireEvent('show', this);
26814 this.fireEvent('hide', this);
26819 Roo.apply(Roo.bootstrap.LocationPicker, {
26821 OverlayView : function(map, options)
26823 options = options || {};
26837 * @class Roo.bootstrap.Alert
26838 * @extends Roo.bootstrap.Component
26839 * Bootstrap Alert class
26840 * @cfg {String} title The title of alert
26841 * @cfg {String} html The content of alert
26842 * @cfg {String} weight ( success | info | warning | danger )
26843 * @cfg {String} faicon font-awesomeicon
26846 * Create a new alert
26847 * @param {Object} config The config object
26851 Roo.bootstrap.Alert = function(config){
26852 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26856 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26863 getAutoCreate : function()
26872 cls : 'roo-alert-icon'
26877 cls : 'roo-alert-title',
26882 cls : 'roo-alert-text',
26889 cfg.cn[0].cls += ' fa ' + this.faicon;
26893 cfg.cls += ' alert-' + this.weight;
26899 initEvents: function()
26901 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26904 setTitle : function(str)
26906 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26909 setText : function(str)
26911 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26914 setWeight : function(weight)
26917 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26920 this.weight = weight;
26922 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26925 setIcon : function(icon)
26928 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26931 this.faicon = icon;
26933 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26954 * @class Roo.bootstrap.UploadCropbox
26955 * @extends Roo.bootstrap.Component
26956 * Bootstrap UploadCropbox class
26957 * @cfg {String} emptyText show when image has been loaded
26958 * @cfg {String} rotateNotify show when image too small to rotate
26959 * @cfg {Number} errorTimeout default 3000
26960 * @cfg {Number} minWidth default 300
26961 * @cfg {Number} minHeight default 300
26962 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26963 * @cfg {Boolean} isDocument (true|false) default false
26964 * @cfg {String} url action url
26965 * @cfg {String} paramName default 'imageUpload'
26966 * @cfg {String} method default POST
26967 * @cfg {Boolean} loadMask (true|false) default true
26968 * @cfg {Boolean} loadingText default 'Loading...'
26971 * Create a new UploadCropbox
26972 * @param {Object} config The config object
26975 Roo.bootstrap.UploadCropbox = function(config){
26976 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26980 * @event beforeselectfile
26981 * Fire before select file
26982 * @param {Roo.bootstrap.UploadCropbox} this
26984 "beforeselectfile" : true,
26987 * Fire after initEvent
26988 * @param {Roo.bootstrap.UploadCropbox} this
26993 * Fire after initEvent
26994 * @param {Roo.bootstrap.UploadCropbox} this
26995 * @param {String} data
27000 * Fire when preparing the file data
27001 * @param {Roo.bootstrap.UploadCropbox} this
27002 * @param {Object} file
27007 * Fire when get exception
27008 * @param {Roo.bootstrap.UploadCropbox} this
27009 * @param {XMLHttpRequest} xhr
27011 "exception" : true,
27013 * @event beforeloadcanvas
27014 * Fire before load the canvas
27015 * @param {Roo.bootstrap.UploadCropbox} this
27016 * @param {String} src
27018 "beforeloadcanvas" : true,
27021 * Fire when trash image
27022 * @param {Roo.bootstrap.UploadCropbox} this
27027 * Fire when download the image
27028 * @param {Roo.bootstrap.UploadCropbox} this
27032 * @event footerbuttonclick
27033 * Fire when footerbuttonclick
27034 * @param {Roo.bootstrap.UploadCropbox} this
27035 * @param {String} type
27037 "footerbuttonclick" : true,
27041 * @param {Roo.bootstrap.UploadCropbox} this
27046 * Fire when rotate the image
27047 * @param {Roo.bootstrap.UploadCropbox} this
27048 * @param {String} pos
27053 * Fire when inspect the file
27054 * @param {Roo.bootstrap.UploadCropbox} this
27055 * @param {Object} file
27060 * Fire when xhr upload the file
27061 * @param {Roo.bootstrap.UploadCropbox} this
27062 * @param {Object} data
27067 * Fire when arrange the file data
27068 * @param {Roo.bootstrap.UploadCropbox} this
27069 * @param {Object} formData
27074 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27077 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27079 emptyText : 'Click to upload image',
27080 rotateNotify : 'Image is too small to rotate',
27081 errorTimeout : 3000,
27095 cropType : 'image/jpeg',
27097 canvasLoaded : false,
27098 isDocument : false,
27100 paramName : 'imageUpload',
27102 loadingText : 'Loading...',
27105 getAutoCreate : function()
27109 cls : 'roo-upload-cropbox',
27113 cls : 'roo-upload-cropbox-selector',
27118 cls : 'roo-upload-cropbox-body',
27119 style : 'cursor:pointer',
27123 cls : 'roo-upload-cropbox-preview'
27127 cls : 'roo-upload-cropbox-thumb'
27131 cls : 'roo-upload-cropbox-empty-notify',
27132 html : this.emptyText
27136 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27137 html : this.rotateNotify
27143 cls : 'roo-upload-cropbox-footer',
27146 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27156 onRender : function(ct, position)
27158 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27160 if (this.buttons.length) {
27162 Roo.each(this.buttons, function(bb) {
27164 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27166 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27172 this.maskEl = this.el;
27176 initEvents : function()
27178 this.urlAPI = (window.createObjectURL && window) ||
27179 (window.URL && URL.revokeObjectURL && URL) ||
27180 (window.webkitURL && webkitURL);
27182 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27183 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27185 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27186 this.selectorEl.hide();
27188 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27189 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27191 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27192 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27193 this.thumbEl.hide();
27195 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27196 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27198 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27199 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27200 this.errorEl.hide();
27202 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27203 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27204 this.footerEl.hide();
27206 this.setThumbBoxSize();
27212 this.fireEvent('initial', this);
27219 window.addEventListener("resize", function() { _this.resize(); } );
27221 this.bodyEl.on('click', this.beforeSelectFile, this);
27224 this.bodyEl.on('touchstart', this.onTouchStart, this);
27225 this.bodyEl.on('touchmove', this.onTouchMove, this);
27226 this.bodyEl.on('touchend', this.onTouchEnd, this);
27230 this.bodyEl.on('mousedown', this.onMouseDown, this);
27231 this.bodyEl.on('mousemove', this.onMouseMove, this);
27232 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27233 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27234 Roo.get(document).on('mouseup', this.onMouseUp, this);
27237 this.selectorEl.on('change', this.onFileSelected, this);
27243 this.baseScale = 1;
27245 this.baseRotate = 1;
27246 this.dragable = false;
27247 this.pinching = false;
27250 this.cropData = false;
27251 this.notifyEl.dom.innerHTML = this.emptyText;
27253 this.selectorEl.dom.value = '';
27257 resize : function()
27259 if(this.fireEvent('resize', this) != false){
27260 this.setThumbBoxPosition();
27261 this.setCanvasPosition();
27265 onFooterButtonClick : function(e, el, o, type)
27268 case 'rotate-left' :
27269 this.onRotateLeft(e);
27271 case 'rotate-right' :
27272 this.onRotateRight(e);
27275 this.beforeSelectFile(e);
27290 this.fireEvent('footerbuttonclick', this, type);
27293 beforeSelectFile : function(e)
27295 e.preventDefault();
27297 if(this.fireEvent('beforeselectfile', this) != false){
27298 this.selectorEl.dom.click();
27302 onFileSelected : function(e)
27304 e.preventDefault();
27306 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27310 var file = this.selectorEl.dom.files[0];
27312 if(this.fireEvent('inspect', this, file) != false){
27313 this.prepare(file);
27318 trash : function(e)
27320 this.fireEvent('trash', this);
27323 download : function(e)
27325 this.fireEvent('download', this);
27328 loadCanvas : function(src)
27330 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27334 this.imageEl = document.createElement('img');
27338 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27340 this.imageEl.src = src;
27344 onLoadCanvas : function()
27346 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27347 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27349 this.bodyEl.un('click', this.beforeSelectFile, this);
27351 this.notifyEl.hide();
27352 this.thumbEl.show();
27353 this.footerEl.show();
27355 this.baseRotateLevel();
27357 if(this.isDocument){
27358 this.setThumbBoxSize();
27361 this.setThumbBoxPosition();
27363 this.baseScaleLevel();
27369 this.canvasLoaded = true;
27372 this.maskEl.unmask();
27377 setCanvasPosition : function()
27379 if(!this.canvasEl){
27383 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27384 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27386 this.previewEl.setLeft(pw);
27387 this.previewEl.setTop(ph);
27391 onMouseDown : function(e)
27395 this.dragable = true;
27396 this.pinching = false;
27398 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27399 this.dragable = false;
27403 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27404 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27408 onMouseMove : function(e)
27412 if(!this.canvasLoaded){
27416 if (!this.dragable){
27420 var minX = Math.ceil(this.thumbEl.getLeft(true));
27421 var minY = Math.ceil(this.thumbEl.getTop(true));
27423 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27424 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27426 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27427 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27429 x = x - this.mouseX;
27430 y = y - this.mouseY;
27432 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27433 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27435 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27436 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27438 this.previewEl.setLeft(bgX);
27439 this.previewEl.setTop(bgY);
27441 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27442 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27445 onMouseUp : function(e)
27449 this.dragable = false;
27452 onMouseWheel : function(e)
27456 this.startScale = this.scale;
27458 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27460 if(!this.zoomable()){
27461 this.scale = this.startScale;
27470 zoomable : function()
27472 var minScale = this.thumbEl.getWidth() / this.minWidth;
27474 if(this.minWidth < this.minHeight){
27475 minScale = this.thumbEl.getHeight() / this.minHeight;
27478 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27479 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27483 (this.rotate == 0 || this.rotate == 180) &&
27485 width > this.imageEl.OriginWidth ||
27486 height > this.imageEl.OriginHeight ||
27487 (width < this.minWidth && height < this.minHeight)
27495 (this.rotate == 90 || this.rotate == 270) &&
27497 width > this.imageEl.OriginWidth ||
27498 height > this.imageEl.OriginHeight ||
27499 (width < this.minHeight && height < this.minWidth)
27506 !this.isDocument &&
27507 (this.rotate == 0 || this.rotate == 180) &&
27509 width < this.minWidth ||
27510 width > this.imageEl.OriginWidth ||
27511 height < this.minHeight ||
27512 height > this.imageEl.OriginHeight
27519 !this.isDocument &&
27520 (this.rotate == 90 || this.rotate == 270) &&
27522 width < this.minHeight ||
27523 width > this.imageEl.OriginWidth ||
27524 height < this.minWidth ||
27525 height > this.imageEl.OriginHeight
27535 onRotateLeft : function(e)
27537 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27539 var minScale = this.thumbEl.getWidth() / this.minWidth;
27541 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27542 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27544 this.startScale = this.scale;
27546 while (this.getScaleLevel() < minScale){
27548 this.scale = this.scale + 1;
27550 if(!this.zoomable()){
27555 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27556 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27561 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27568 this.scale = this.startScale;
27570 this.onRotateFail();
27575 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27577 if(this.isDocument){
27578 this.setThumbBoxSize();
27579 this.setThumbBoxPosition();
27580 this.setCanvasPosition();
27585 this.fireEvent('rotate', this, 'left');
27589 onRotateRight : function(e)
27591 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27593 var minScale = this.thumbEl.getWidth() / this.minWidth;
27595 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27596 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27598 this.startScale = this.scale;
27600 while (this.getScaleLevel() < minScale){
27602 this.scale = this.scale + 1;
27604 if(!this.zoomable()){
27609 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27610 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27615 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27622 this.scale = this.startScale;
27624 this.onRotateFail();
27629 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27631 if(this.isDocument){
27632 this.setThumbBoxSize();
27633 this.setThumbBoxPosition();
27634 this.setCanvasPosition();
27639 this.fireEvent('rotate', this, 'right');
27642 onRotateFail : function()
27644 this.errorEl.show(true);
27648 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27653 this.previewEl.dom.innerHTML = '';
27655 var canvasEl = document.createElement("canvas");
27657 var contextEl = canvasEl.getContext("2d");
27659 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27660 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27661 var center = this.imageEl.OriginWidth / 2;
27663 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27664 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27665 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27666 center = this.imageEl.OriginHeight / 2;
27669 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27671 contextEl.translate(center, center);
27672 contextEl.rotate(this.rotate * Math.PI / 180);
27674 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27676 this.canvasEl = document.createElement("canvas");
27678 this.contextEl = this.canvasEl.getContext("2d");
27680 switch (this.rotate) {
27683 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27684 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27686 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27691 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27692 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27694 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27695 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);
27699 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27704 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27705 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27707 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27708 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);
27712 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);
27717 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27718 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27720 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27721 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27725 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);
27732 this.previewEl.appendChild(this.canvasEl);
27734 this.setCanvasPosition();
27739 if(!this.canvasLoaded){
27743 var imageCanvas = document.createElement("canvas");
27745 var imageContext = imageCanvas.getContext("2d");
27747 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27748 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27750 var center = imageCanvas.width / 2;
27752 imageContext.translate(center, center);
27754 imageContext.rotate(this.rotate * Math.PI / 180);
27756 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27758 var canvas = document.createElement("canvas");
27760 var context = canvas.getContext("2d");
27762 canvas.width = this.minWidth;
27763 canvas.height = this.minHeight;
27765 switch (this.rotate) {
27768 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27769 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27771 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27772 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27774 var targetWidth = this.minWidth - 2 * x;
27775 var targetHeight = this.minHeight - 2 * y;
27779 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27780 scale = targetWidth / width;
27783 if(x > 0 && y == 0){
27784 scale = targetHeight / height;
27787 if(x > 0 && y > 0){
27788 scale = targetWidth / width;
27790 if(width < height){
27791 scale = targetHeight / height;
27795 context.scale(scale, scale);
27797 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27798 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27800 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27801 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27803 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27808 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27809 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27811 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27812 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27814 var targetWidth = this.minWidth - 2 * x;
27815 var targetHeight = this.minHeight - 2 * y;
27819 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27820 scale = targetWidth / width;
27823 if(x > 0 && y == 0){
27824 scale = targetHeight / height;
27827 if(x > 0 && y > 0){
27828 scale = targetWidth / width;
27830 if(width < height){
27831 scale = targetHeight / height;
27835 context.scale(scale, scale);
27837 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27838 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27840 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27841 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27843 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27845 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27850 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27851 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27853 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27854 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27856 var targetWidth = this.minWidth - 2 * x;
27857 var targetHeight = this.minHeight - 2 * y;
27861 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27862 scale = targetWidth / width;
27865 if(x > 0 && y == 0){
27866 scale = targetHeight / height;
27869 if(x > 0 && y > 0){
27870 scale = targetWidth / width;
27872 if(width < height){
27873 scale = targetHeight / height;
27877 context.scale(scale, scale);
27879 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27880 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27882 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27883 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27885 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27886 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27888 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27893 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27894 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27896 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27897 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27899 var targetWidth = this.minWidth - 2 * x;
27900 var targetHeight = this.minHeight - 2 * y;
27904 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27905 scale = targetWidth / width;
27908 if(x > 0 && y == 0){
27909 scale = targetHeight / height;
27912 if(x > 0 && y > 0){
27913 scale = targetWidth / width;
27915 if(width < height){
27916 scale = targetHeight / height;
27920 context.scale(scale, scale);
27922 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27923 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27925 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27926 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27928 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27930 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27937 this.cropData = canvas.toDataURL(this.cropType);
27939 if(this.fireEvent('crop', this, this.cropData) !== false){
27940 this.process(this.file, this.cropData);
27947 setThumbBoxSize : function()
27951 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27952 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27953 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27955 this.minWidth = width;
27956 this.minHeight = height;
27958 if(this.rotate == 90 || this.rotate == 270){
27959 this.minWidth = height;
27960 this.minHeight = width;
27965 width = Math.ceil(this.minWidth * height / this.minHeight);
27967 if(this.minWidth > this.minHeight){
27969 height = Math.ceil(this.minHeight * width / this.minWidth);
27972 this.thumbEl.setStyle({
27973 width : width + 'px',
27974 height : height + 'px'
27981 setThumbBoxPosition : function()
27983 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27984 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27986 this.thumbEl.setLeft(x);
27987 this.thumbEl.setTop(y);
27991 baseRotateLevel : function()
27993 this.baseRotate = 1;
27996 typeof(this.exif) != 'undefined' &&
27997 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27998 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28000 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28003 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28007 baseScaleLevel : function()
28011 if(this.isDocument){
28013 if(this.baseRotate == 6 || this.baseRotate == 8){
28015 height = this.thumbEl.getHeight();
28016 this.baseScale = height / this.imageEl.OriginWidth;
28018 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28019 width = this.thumbEl.getWidth();
28020 this.baseScale = width / this.imageEl.OriginHeight;
28026 height = this.thumbEl.getHeight();
28027 this.baseScale = height / this.imageEl.OriginHeight;
28029 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28030 width = this.thumbEl.getWidth();
28031 this.baseScale = width / this.imageEl.OriginWidth;
28037 if(this.baseRotate == 6 || this.baseRotate == 8){
28039 width = this.thumbEl.getHeight();
28040 this.baseScale = width / this.imageEl.OriginHeight;
28042 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28043 height = this.thumbEl.getWidth();
28044 this.baseScale = height / this.imageEl.OriginHeight;
28047 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28048 height = this.thumbEl.getWidth();
28049 this.baseScale = height / this.imageEl.OriginHeight;
28051 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28052 width = this.thumbEl.getHeight();
28053 this.baseScale = width / this.imageEl.OriginWidth;
28060 width = this.thumbEl.getWidth();
28061 this.baseScale = width / this.imageEl.OriginWidth;
28063 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28064 height = this.thumbEl.getHeight();
28065 this.baseScale = height / this.imageEl.OriginHeight;
28068 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28070 height = this.thumbEl.getHeight();
28071 this.baseScale = height / this.imageEl.OriginHeight;
28073 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28074 width = this.thumbEl.getWidth();
28075 this.baseScale = width / this.imageEl.OriginWidth;
28083 getScaleLevel : function()
28085 return this.baseScale * Math.pow(1.1, this.scale);
28088 onTouchStart : function(e)
28090 if(!this.canvasLoaded){
28091 this.beforeSelectFile(e);
28095 var touches = e.browserEvent.touches;
28101 if(touches.length == 1){
28102 this.onMouseDown(e);
28106 if(touches.length != 2){
28112 for(var i = 0, finger; finger = touches[i]; i++){
28113 coords.push(finger.pageX, finger.pageY);
28116 var x = Math.pow(coords[0] - coords[2], 2);
28117 var y = Math.pow(coords[1] - coords[3], 2);
28119 this.startDistance = Math.sqrt(x + y);
28121 this.startScale = this.scale;
28123 this.pinching = true;
28124 this.dragable = false;
28128 onTouchMove : function(e)
28130 if(!this.pinching && !this.dragable){
28134 var touches = e.browserEvent.touches;
28141 this.onMouseMove(e);
28147 for(var i = 0, finger; finger = touches[i]; i++){
28148 coords.push(finger.pageX, finger.pageY);
28151 var x = Math.pow(coords[0] - coords[2], 2);
28152 var y = Math.pow(coords[1] - coords[3], 2);
28154 this.endDistance = Math.sqrt(x + y);
28156 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28158 if(!this.zoomable()){
28159 this.scale = this.startScale;
28167 onTouchEnd : function(e)
28169 this.pinching = false;
28170 this.dragable = false;
28174 process : function(file, crop)
28177 this.maskEl.mask(this.loadingText);
28180 this.xhr = new XMLHttpRequest();
28182 file.xhr = this.xhr;
28184 this.xhr.open(this.method, this.url, true);
28187 "Accept": "application/json",
28188 "Cache-Control": "no-cache",
28189 "X-Requested-With": "XMLHttpRequest"
28192 for (var headerName in headers) {
28193 var headerValue = headers[headerName];
28195 this.xhr.setRequestHeader(headerName, headerValue);
28201 this.xhr.onload = function()
28203 _this.xhrOnLoad(_this.xhr);
28206 this.xhr.onerror = function()
28208 _this.xhrOnError(_this.xhr);
28211 var formData = new FormData();
28213 formData.append('returnHTML', 'NO');
28216 formData.append('crop', crop);
28219 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28220 formData.append(this.paramName, file, file.name);
28223 if(typeof(file.filename) != 'undefined'){
28224 formData.append('filename', file.filename);
28227 if(typeof(file.mimetype) != 'undefined'){
28228 formData.append('mimetype', file.mimetype);
28231 if(this.fireEvent('arrange', this, formData) != false){
28232 this.xhr.send(formData);
28236 xhrOnLoad : function(xhr)
28239 this.maskEl.unmask();
28242 if (xhr.readyState !== 4) {
28243 this.fireEvent('exception', this, xhr);
28247 var response = Roo.decode(xhr.responseText);
28249 if(!response.success){
28250 this.fireEvent('exception', this, xhr);
28254 var response = Roo.decode(xhr.responseText);
28256 this.fireEvent('upload', this, response);
28260 xhrOnError : function()
28263 this.maskEl.unmask();
28266 Roo.log('xhr on error');
28268 var response = Roo.decode(xhr.responseText);
28274 prepare : function(file)
28277 this.maskEl.mask(this.loadingText);
28283 if(typeof(file) === 'string'){
28284 this.loadCanvas(file);
28288 if(!file || !this.urlAPI){
28293 this.cropType = file.type;
28297 if(this.fireEvent('prepare', this, this.file) != false){
28299 var reader = new FileReader();
28301 reader.onload = function (e) {
28302 if (e.target.error) {
28303 Roo.log(e.target.error);
28307 var buffer = e.target.result,
28308 dataView = new DataView(buffer),
28310 maxOffset = dataView.byteLength - 4,
28314 if (dataView.getUint16(0) === 0xffd8) {
28315 while (offset < maxOffset) {
28316 markerBytes = dataView.getUint16(offset);
28318 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28319 markerLength = dataView.getUint16(offset + 2) + 2;
28320 if (offset + markerLength > dataView.byteLength) {
28321 Roo.log('Invalid meta data: Invalid segment size.');
28325 if(markerBytes == 0xffe1){
28326 _this.parseExifData(
28333 offset += markerLength;
28343 var url = _this.urlAPI.createObjectURL(_this.file);
28345 _this.loadCanvas(url);
28350 reader.readAsArrayBuffer(this.file);
28356 parseExifData : function(dataView, offset, length)
28358 var tiffOffset = offset + 10,
28362 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28363 // No Exif data, might be XMP data instead
28367 // Check for the ASCII code for "Exif" (0x45786966):
28368 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28369 // No Exif data, might be XMP data instead
28372 if (tiffOffset + 8 > dataView.byteLength) {
28373 Roo.log('Invalid Exif data: Invalid segment size.');
28376 // Check for the two null bytes:
28377 if (dataView.getUint16(offset + 8) !== 0x0000) {
28378 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28381 // Check the byte alignment:
28382 switch (dataView.getUint16(tiffOffset)) {
28384 littleEndian = true;
28387 littleEndian = false;
28390 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28393 // Check for the TIFF tag marker (0x002A):
28394 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28395 Roo.log('Invalid Exif data: Missing TIFF marker.');
28398 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28399 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28401 this.parseExifTags(
28404 tiffOffset + dirOffset,
28409 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28414 if (dirOffset + 6 > dataView.byteLength) {
28415 Roo.log('Invalid Exif data: Invalid directory offset.');
28418 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28419 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28420 if (dirEndOffset + 4 > dataView.byteLength) {
28421 Roo.log('Invalid Exif data: Invalid directory size.');
28424 for (i = 0; i < tagsNumber; i += 1) {
28428 dirOffset + 2 + 12 * i, // tag offset
28432 // Return the offset to the next directory:
28433 return dataView.getUint32(dirEndOffset, littleEndian);
28436 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28438 var tag = dataView.getUint16(offset, littleEndian);
28440 this.exif[tag] = this.getExifValue(
28444 dataView.getUint16(offset + 2, littleEndian), // tag type
28445 dataView.getUint32(offset + 4, littleEndian), // tag length
28450 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28452 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28461 Roo.log('Invalid Exif data: Invalid tag type.');
28465 tagSize = tagType.size * length;
28466 // Determine if the value is contained in the dataOffset bytes,
28467 // or if the value at the dataOffset is a pointer to the actual data:
28468 dataOffset = tagSize > 4 ?
28469 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28470 if (dataOffset + tagSize > dataView.byteLength) {
28471 Roo.log('Invalid Exif data: Invalid data offset.');
28474 if (length === 1) {
28475 return tagType.getValue(dataView, dataOffset, littleEndian);
28478 for (i = 0; i < length; i += 1) {
28479 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28482 if (tagType.ascii) {
28484 // Concatenate the chars:
28485 for (i = 0; i < values.length; i += 1) {
28487 // Ignore the terminating NULL byte(s):
28488 if (c === '\u0000') {
28500 Roo.apply(Roo.bootstrap.UploadCropbox, {
28502 'Orientation': 0x0112
28506 1: 0, //'top-left',
28508 3: 180, //'bottom-right',
28509 // 4: 'bottom-left',
28511 6: 90, //'right-top',
28512 // 7: 'right-bottom',
28513 8: 270 //'left-bottom'
28517 // byte, 8-bit unsigned int:
28519 getValue: function (dataView, dataOffset) {
28520 return dataView.getUint8(dataOffset);
28524 // ascii, 8-bit byte:
28526 getValue: function (dataView, dataOffset) {
28527 return String.fromCharCode(dataView.getUint8(dataOffset));
28532 // short, 16 bit int:
28534 getValue: function (dataView, dataOffset, littleEndian) {
28535 return dataView.getUint16(dataOffset, littleEndian);
28539 // long, 32 bit int:
28541 getValue: function (dataView, dataOffset, littleEndian) {
28542 return dataView.getUint32(dataOffset, littleEndian);
28546 // rational = two long values, first is numerator, second is denominator:
28548 getValue: function (dataView, dataOffset, littleEndian) {
28549 return dataView.getUint32(dataOffset, littleEndian) /
28550 dataView.getUint32(dataOffset + 4, littleEndian);
28554 // slong, 32 bit signed int:
28556 getValue: function (dataView, dataOffset, littleEndian) {
28557 return dataView.getInt32(dataOffset, littleEndian);
28561 // srational, two slongs, first is numerator, second is denominator:
28563 getValue: function (dataView, dataOffset, littleEndian) {
28564 return dataView.getInt32(dataOffset, littleEndian) /
28565 dataView.getInt32(dataOffset + 4, littleEndian);
28575 cls : 'btn-group roo-upload-cropbox-rotate-left',
28576 action : 'rotate-left',
28580 cls : 'btn btn-default',
28581 html : '<i class="fa fa-undo"></i>'
28587 cls : 'btn-group roo-upload-cropbox-picture',
28588 action : 'picture',
28592 cls : 'btn btn-default',
28593 html : '<i class="fa fa-picture-o"></i>'
28599 cls : 'btn-group roo-upload-cropbox-rotate-right',
28600 action : 'rotate-right',
28604 cls : 'btn btn-default',
28605 html : '<i class="fa fa-repeat"></i>'
28613 cls : 'btn-group roo-upload-cropbox-rotate-left',
28614 action : 'rotate-left',
28618 cls : 'btn btn-default',
28619 html : '<i class="fa fa-undo"></i>'
28625 cls : 'btn-group roo-upload-cropbox-download',
28626 action : 'download',
28630 cls : 'btn btn-default',
28631 html : '<i class="fa fa-download"></i>'
28637 cls : 'btn-group roo-upload-cropbox-crop',
28642 cls : 'btn btn-default',
28643 html : '<i class="fa fa-crop"></i>'
28649 cls : 'btn-group roo-upload-cropbox-trash',
28654 cls : 'btn btn-default',
28655 html : '<i class="fa fa-trash"></i>'
28661 cls : 'btn-group roo-upload-cropbox-rotate-right',
28662 action : 'rotate-right',
28666 cls : 'btn btn-default',
28667 html : '<i class="fa fa-repeat"></i>'
28675 cls : 'btn-group roo-upload-cropbox-rotate-left',
28676 action : 'rotate-left',
28680 cls : 'btn btn-default',
28681 html : '<i class="fa fa-undo"></i>'
28687 cls : 'btn-group roo-upload-cropbox-rotate-right',
28688 action : 'rotate-right',
28692 cls : 'btn btn-default',
28693 html : '<i class="fa fa-repeat"></i>'
28706 * @class Roo.bootstrap.DocumentManager
28707 * @extends Roo.bootstrap.Component
28708 * Bootstrap DocumentManager class
28709 * @cfg {String} paramName default 'imageUpload'
28710 * @cfg {String} toolTipName default 'filename'
28711 * @cfg {String} method default POST
28712 * @cfg {String} url action url
28713 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28714 * @cfg {Boolean} multiple multiple upload default true
28715 * @cfg {Number} thumbSize default 300
28716 * @cfg {String} fieldLabel
28717 * @cfg {Number} labelWidth default 4
28718 * @cfg {String} labelAlign (left|top) default left
28719 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28720 * @cfg {Number} labellg set the width of label (1-12)
28721 * @cfg {Number} labelmd set the width of label (1-12)
28722 * @cfg {Number} labelsm set the width of label (1-12)
28723 * @cfg {Number} labelxs set the width of label (1-12)
28726 * Create a new DocumentManager
28727 * @param {Object} config The config object
28730 Roo.bootstrap.DocumentManager = function(config){
28731 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28734 this.delegates = [];
28739 * Fire when initial the DocumentManager
28740 * @param {Roo.bootstrap.DocumentManager} this
28745 * inspect selected file
28746 * @param {Roo.bootstrap.DocumentManager} this
28747 * @param {File} file
28752 * Fire when xhr load exception
28753 * @param {Roo.bootstrap.DocumentManager} this
28754 * @param {XMLHttpRequest} xhr
28756 "exception" : true,
28758 * @event afterupload
28759 * Fire when xhr load exception
28760 * @param {Roo.bootstrap.DocumentManager} this
28761 * @param {XMLHttpRequest} xhr
28763 "afterupload" : true,
28766 * prepare the form data
28767 * @param {Roo.bootstrap.DocumentManager} this
28768 * @param {Object} formData
28773 * Fire when remove the file
28774 * @param {Roo.bootstrap.DocumentManager} this
28775 * @param {Object} file
28780 * Fire after refresh the file
28781 * @param {Roo.bootstrap.DocumentManager} this
28786 * Fire after click the image
28787 * @param {Roo.bootstrap.DocumentManager} this
28788 * @param {Object} file
28793 * Fire when upload a image and editable set to true
28794 * @param {Roo.bootstrap.DocumentManager} this
28795 * @param {Object} file
28799 * @event beforeselectfile
28800 * Fire before select file
28801 * @param {Roo.bootstrap.DocumentManager} this
28803 "beforeselectfile" : true,
28806 * Fire before process file
28807 * @param {Roo.bootstrap.DocumentManager} this
28808 * @param {Object} file
28812 * @event previewrendered
28813 * Fire when preview rendered
28814 * @param {Roo.bootstrap.DocumentManager} this
28815 * @param {Object} file
28817 "previewrendered" : true,
28820 "previewResize" : true
28825 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28834 paramName : 'imageUpload',
28835 toolTipName : 'filename',
28838 labelAlign : 'left',
28848 getAutoCreate : function()
28850 var managerWidget = {
28852 cls : 'roo-document-manager',
28856 cls : 'roo-document-manager-selector',
28861 cls : 'roo-document-manager-uploader',
28865 cls : 'roo-document-manager-upload-btn',
28866 html : '<i class="fa fa-plus"></i>'
28877 cls : 'column col-md-12',
28882 if(this.fieldLabel.length){
28887 cls : 'column col-md-12',
28888 html : this.fieldLabel
28892 cls : 'column col-md-12',
28897 if(this.labelAlign == 'left'){
28902 html : this.fieldLabel
28911 if(this.labelWidth > 12){
28912 content[0].style = "width: " + this.labelWidth + 'px';
28915 if(this.labelWidth < 13 && this.labelmd == 0){
28916 this.labelmd = this.labelWidth;
28919 if(this.labellg > 0){
28920 content[0].cls += ' col-lg-' + this.labellg;
28921 content[1].cls += ' col-lg-' + (12 - this.labellg);
28924 if(this.labelmd > 0){
28925 content[0].cls += ' col-md-' + this.labelmd;
28926 content[1].cls += ' col-md-' + (12 - this.labelmd);
28929 if(this.labelsm > 0){
28930 content[0].cls += ' col-sm-' + this.labelsm;
28931 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28934 if(this.labelxs > 0){
28935 content[0].cls += ' col-xs-' + this.labelxs;
28936 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28944 cls : 'row clearfix',
28952 initEvents : function()
28954 this.managerEl = this.el.select('.roo-document-manager', true).first();
28955 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28957 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28958 this.selectorEl.hide();
28961 this.selectorEl.attr('multiple', 'multiple');
28964 this.selectorEl.on('change', this.onFileSelected, this);
28966 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28967 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28969 this.uploader.on('click', this.onUploaderClick, this);
28971 this.renderProgressDialog();
28975 window.addEventListener("resize", function() { _this.refresh(); } );
28977 this.fireEvent('initial', this);
28980 renderProgressDialog : function()
28984 this.progressDialog = new Roo.bootstrap.Modal({
28985 cls : 'roo-document-manager-progress-dialog',
28986 allow_close : false,
28996 btnclick : function() {
28997 _this.uploadCancel();
29003 this.progressDialog.render(Roo.get(document.body));
29005 this.progress = new Roo.bootstrap.Progress({
29006 cls : 'roo-document-manager-progress',
29011 this.progress.render(this.progressDialog.getChildContainer());
29013 this.progressBar = new Roo.bootstrap.ProgressBar({
29014 cls : 'roo-document-manager-progress-bar',
29017 aria_valuemax : 12,
29021 this.progressBar.render(this.progress.getChildContainer());
29024 onUploaderClick : function(e)
29026 e.preventDefault();
29028 if(this.fireEvent('beforeselectfile', this) != false){
29029 this.selectorEl.dom.click();
29034 onFileSelected : function(e)
29036 e.preventDefault();
29038 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29042 Roo.each(this.selectorEl.dom.files, function(file){
29043 if(this.fireEvent('inspect', this, file) != false){
29044 this.files.push(file);
29054 this.selectorEl.dom.value = '';
29056 if(!this.files || !this.files.length){
29060 if(this.boxes > 0 && this.files.length > this.boxes){
29061 this.files = this.files.slice(0, this.boxes);
29064 this.uploader.show();
29066 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29067 this.uploader.hide();
29076 Roo.each(this.files, function(file){
29078 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29079 var f = this.renderPreview(file);
29084 if(file.type.indexOf('image') != -1){
29085 this.delegates.push(
29087 _this.process(file);
29088 }).createDelegate(this)
29096 _this.process(file);
29097 }).createDelegate(this)
29102 this.files = files;
29104 this.delegates = this.delegates.concat(docs);
29106 if(!this.delegates.length){
29111 this.progressBar.aria_valuemax = this.delegates.length;
29118 arrange : function()
29120 if(!this.delegates.length){
29121 this.progressDialog.hide();
29126 var delegate = this.delegates.shift();
29128 this.progressDialog.show();
29130 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29132 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29137 refresh : function()
29139 this.uploader.show();
29141 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29142 this.uploader.hide();
29145 Roo.isTouch ? this.closable(false) : this.closable(true);
29147 this.fireEvent('refresh', this);
29150 onRemove : function(e, el, o)
29152 e.preventDefault();
29154 this.fireEvent('remove', this, o);
29158 remove : function(o)
29162 Roo.each(this.files, function(file){
29163 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29172 this.files = files;
29179 Roo.each(this.files, function(file){
29184 file.target.remove();
29193 onClick : function(e, el, o)
29195 e.preventDefault();
29197 this.fireEvent('click', this, o);
29201 closable : function(closable)
29203 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29205 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29217 xhrOnLoad : function(xhr)
29219 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29223 if (xhr.readyState !== 4) {
29225 this.fireEvent('exception', this, xhr);
29229 var response = Roo.decode(xhr.responseText);
29231 if(!response.success){
29233 this.fireEvent('exception', this, xhr);
29237 var file = this.renderPreview(response.data);
29239 this.files.push(file);
29243 this.fireEvent('afterupload', this, xhr);
29247 xhrOnError : function(xhr)
29249 Roo.log('xhr on error');
29251 var response = Roo.decode(xhr.responseText);
29258 process : function(file)
29260 if(this.fireEvent('process', this, file) !== false){
29261 if(this.editable && file.type.indexOf('image') != -1){
29262 this.fireEvent('edit', this, file);
29266 this.uploadStart(file, false);
29273 uploadStart : function(file, crop)
29275 this.xhr = new XMLHttpRequest();
29277 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29282 file.xhr = this.xhr;
29284 this.managerEl.createChild({
29286 cls : 'roo-document-manager-loading',
29290 tooltip : file.name,
29291 cls : 'roo-document-manager-thumb',
29292 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29298 this.xhr.open(this.method, this.url, true);
29301 "Accept": "application/json",
29302 "Cache-Control": "no-cache",
29303 "X-Requested-With": "XMLHttpRequest"
29306 for (var headerName in headers) {
29307 var headerValue = headers[headerName];
29309 this.xhr.setRequestHeader(headerName, headerValue);
29315 this.xhr.onload = function()
29317 _this.xhrOnLoad(_this.xhr);
29320 this.xhr.onerror = function()
29322 _this.xhrOnError(_this.xhr);
29325 var formData = new FormData();
29327 formData.append('returnHTML', 'NO');
29330 formData.append('crop', crop);
29333 formData.append(this.paramName, file, file.name);
29340 if(this.fireEvent('prepare', this, formData, options) != false){
29342 if(options.manually){
29346 this.xhr.send(formData);
29350 this.uploadCancel();
29353 uploadCancel : function()
29359 this.delegates = [];
29361 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29368 renderPreview : function(file)
29370 if(typeof(file.target) != 'undefined' && file.target){
29374 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29376 var previewEl = this.managerEl.createChild({
29378 cls : 'roo-document-manager-preview',
29382 tooltip : file[this.toolTipName],
29383 cls : 'roo-document-manager-thumb',
29384 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29389 html : '<i class="fa fa-times-circle"></i>'
29394 var close = previewEl.select('button.close', true).first();
29396 close.on('click', this.onRemove, this, file);
29398 file.target = previewEl;
29400 var image = previewEl.select('img', true).first();
29404 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29406 image.on('click', this.onClick, this, file);
29408 this.fireEvent('previewrendered', this, file);
29414 onPreviewLoad : function(file, image)
29416 if(typeof(file.target) == 'undefined' || !file.target){
29420 var width = image.dom.naturalWidth || image.dom.width;
29421 var height = image.dom.naturalHeight || image.dom.height;
29423 if(!this.previewResize) {
29427 if(width > height){
29428 file.target.addClass('wide');
29432 file.target.addClass('tall');
29437 uploadFromSource : function(file, crop)
29439 this.xhr = new XMLHttpRequest();
29441 this.managerEl.createChild({
29443 cls : 'roo-document-manager-loading',
29447 tooltip : file.name,
29448 cls : 'roo-document-manager-thumb',
29449 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29455 this.xhr.open(this.method, this.url, true);
29458 "Accept": "application/json",
29459 "Cache-Control": "no-cache",
29460 "X-Requested-With": "XMLHttpRequest"
29463 for (var headerName in headers) {
29464 var headerValue = headers[headerName];
29466 this.xhr.setRequestHeader(headerName, headerValue);
29472 this.xhr.onload = function()
29474 _this.xhrOnLoad(_this.xhr);
29477 this.xhr.onerror = function()
29479 _this.xhrOnError(_this.xhr);
29482 var formData = new FormData();
29484 formData.append('returnHTML', 'NO');
29486 formData.append('crop', crop);
29488 if(typeof(file.filename) != 'undefined'){
29489 formData.append('filename', file.filename);
29492 if(typeof(file.mimetype) != 'undefined'){
29493 formData.append('mimetype', file.mimetype);
29498 if(this.fireEvent('prepare', this, formData) != false){
29499 this.xhr.send(formData);
29509 * @class Roo.bootstrap.DocumentViewer
29510 * @extends Roo.bootstrap.Component
29511 * Bootstrap DocumentViewer class
29512 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29513 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29516 * Create a new DocumentViewer
29517 * @param {Object} config The config object
29520 Roo.bootstrap.DocumentViewer = function(config){
29521 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29526 * Fire after initEvent
29527 * @param {Roo.bootstrap.DocumentViewer} this
29533 * @param {Roo.bootstrap.DocumentViewer} this
29538 * Fire after download button
29539 * @param {Roo.bootstrap.DocumentViewer} this
29544 * Fire after trash button
29545 * @param {Roo.bootstrap.DocumentViewer} this
29552 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29554 showDownload : true,
29558 getAutoCreate : function()
29562 cls : 'roo-document-viewer',
29566 cls : 'roo-document-viewer-body',
29570 cls : 'roo-document-viewer-thumb',
29574 cls : 'roo-document-viewer-image'
29582 cls : 'roo-document-viewer-footer',
29585 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29589 cls : 'btn-group roo-document-viewer-download',
29593 cls : 'btn btn-default',
29594 html : '<i class="fa fa-download"></i>'
29600 cls : 'btn-group roo-document-viewer-trash',
29604 cls : 'btn btn-default',
29605 html : '<i class="fa fa-trash"></i>'
29618 initEvents : function()
29620 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29621 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29623 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29624 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29626 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29627 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29629 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29630 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29632 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29633 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29635 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29636 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29638 this.bodyEl.on('click', this.onClick, this);
29639 this.downloadBtn.on('click', this.onDownload, this);
29640 this.trashBtn.on('click', this.onTrash, this);
29642 this.downloadBtn.hide();
29643 this.trashBtn.hide();
29645 if(this.showDownload){
29646 this.downloadBtn.show();
29649 if(this.showTrash){
29650 this.trashBtn.show();
29653 if(!this.showDownload && !this.showTrash) {
29654 this.footerEl.hide();
29659 initial : function()
29661 this.fireEvent('initial', this);
29665 onClick : function(e)
29667 e.preventDefault();
29669 this.fireEvent('click', this);
29672 onDownload : function(e)
29674 e.preventDefault();
29676 this.fireEvent('download', this);
29679 onTrash : function(e)
29681 e.preventDefault();
29683 this.fireEvent('trash', this);
29695 * @class Roo.bootstrap.NavProgressBar
29696 * @extends Roo.bootstrap.Component
29697 * Bootstrap NavProgressBar class
29700 * Create a new nav progress bar
29701 * @param {Object} config The config object
29704 Roo.bootstrap.NavProgressBar = function(config){
29705 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29707 this.bullets = this.bullets || [];
29709 // Roo.bootstrap.NavProgressBar.register(this);
29713 * Fires when the active item changes
29714 * @param {Roo.bootstrap.NavProgressBar} this
29715 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29716 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29723 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29728 getAutoCreate : function()
29730 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29734 cls : 'roo-navigation-bar-group',
29738 cls : 'roo-navigation-top-bar'
29742 cls : 'roo-navigation-bullets-bar',
29746 cls : 'roo-navigation-bar'
29753 cls : 'roo-navigation-bottom-bar'
29763 initEvents: function()
29768 onRender : function(ct, position)
29770 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29772 if(this.bullets.length){
29773 Roo.each(this.bullets, function(b){
29782 addItem : function(cfg)
29784 var item = new Roo.bootstrap.NavProgressItem(cfg);
29786 item.parentId = this.id;
29787 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29790 var top = new Roo.bootstrap.Element({
29792 cls : 'roo-navigation-bar-text'
29795 var bottom = new Roo.bootstrap.Element({
29797 cls : 'roo-navigation-bar-text'
29800 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29801 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29803 var topText = new Roo.bootstrap.Element({
29805 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29808 var bottomText = new Roo.bootstrap.Element({
29810 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29813 topText.onRender(top.el, null);
29814 bottomText.onRender(bottom.el, null);
29817 item.bottomEl = bottom;
29820 this.barItems.push(item);
29825 getActive : function()
29827 var active = false;
29829 Roo.each(this.barItems, function(v){
29831 if (!v.isActive()) {
29843 setActiveItem : function(item)
29847 Roo.each(this.barItems, function(v){
29848 if (v.rid == item.rid) {
29852 if (v.isActive()) {
29853 v.setActive(false);
29858 item.setActive(true);
29860 this.fireEvent('changed', this, item, prev);
29863 getBarItem: function(rid)
29867 Roo.each(this.barItems, function(e) {
29868 if (e.rid != rid) {
29879 indexOfItem : function(item)
29883 Roo.each(this.barItems, function(v, i){
29885 if (v.rid != item.rid) {
29896 setActiveNext : function()
29898 var i = this.indexOfItem(this.getActive());
29900 if (i > this.barItems.length) {
29904 this.setActiveItem(this.barItems[i+1]);
29907 setActivePrev : function()
29909 var i = this.indexOfItem(this.getActive());
29915 this.setActiveItem(this.barItems[i-1]);
29918 format : function()
29920 if(!this.barItems.length){
29924 var width = 100 / this.barItems.length;
29926 Roo.each(this.barItems, function(i){
29927 i.el.setStyle('width', width + '%');
29928 i.topEl.el.setStyle('width', width + '%');
29929 i.bottomEl.el.setStyle('width', width + '%');
29938 * Nav Progress Item
29943 * @class Roo.bootstrap.NavProgressItem
29944 * @extends Roo.bootstrap.Component
29945 * Bootstrap NavProgressItem class
29946 * @cfg {String} rid the reference id
29947 * @cfg {Boolean} active (true|false) Is item active default false
29948 * @cfg {Boolean} disabled (true|false) Is item active default false
29949 * @cfg {String} html
29950 * @cfg {String} position (top|bottom) text position default bottom
29951 * @cfg {String} icon show icon instead of number
29954 * Create a new NavProgressItem
29955 * @param {Object} config The config object
29957 Roo.bootstrap.NavProgressItem = function(config){
29958 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29963 * The raw click event for the entire grid.
29964 * @param {Roo.bootstrap.NavProgressItem} this
29965 * @param {Roo.EventObject} e
29972 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29978 position : 'bottom',
29981 getAutoCreate : function()
29983 var iconCls = 'roo-navigation-bar-item-icon';
29985 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29989 cls: 'roo-navigation-bar-item',
29999 cfg.cls += ' active';
30002 cfg.cls += ' disabled';
30008 disable : function()
30010 this.setDisabled(true);
30013 enable : function()
30015 this.setDisabled(false);
30018 initEvents: function()
30020 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30022 this.iconEl.on('click', this.onClick, this);
30025 onClick : function(e)
30027 e.preventDefault();
30033 if(this.fireEvent('click', this, e) === false){
30037 this.parent().setActiveItem(this);
30040 isActive: function ()
30042 return this.active;
30045 setActive : function(state)
30047 if(this.active == state){
30051 this.active = state;
30054 this.el.addClass('active');
30058 this.el.removeClass('active');
30063 setDisabled : function(state)
30065 if(this.disabled == state){
30069 this.disabled = state;
30072 this.el.addClass('disabled');
30076 this.el.removeClass('disabled');
30079 tooltipEl : function()
30081 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30094 * @class Roo.bootstrap.FieldLabel
30095 * @extends Roo.bootstrap.Component
30096 * Bootstrap FieldLabel class
30097 * @cfg {String} html contents of the element
30098 * @cfg {String} tag tag of the element default label
30099 * @cfg {String} cls class of the element
30100 * @cfg {String} target label target
30101 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30102 * @cfg {String} invalidClass default "text-warning"
30103 * @cfg {String} validClass default "text-success"
30104 * @cfg {String} iconTooltip default "This field is required"
30105 * @cfg {String} indicatorpos (left|right) default left
30108 * Create a new FieldLabel
30109 * @param {Object} config The config object
30112 Roo.bootstrap.FieldLabel = function(config){
30113 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30118 * Fires after the field has been marked as invalid.
30119 * @param {Roo.form.FieldLabel} this
30120 * @param {String} msg The validation message
30125 * Fires after the field has been validated with no errors.
30126 * @param {Roo.form.FieldLabel} this
30132 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30139 invalidClass : 'has-warning',
30140 validClass : 'has-success',
30141 iconTooltip : 'This field is required',
30142 indicatorpos : 'left',
30144 getAutoCreate : function(){
30147 if (!this.allowBlank) {
30153 cls : 'roo-bootstrap-field-label ' + this.cls,
30158 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30159 tooltip : this.iconTooltip
30168 if(this.indicatorpos == 'right'){
30171 cls : 'roo-bootstrap-field-label ' + this.cls,
30180 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30181 tooltip : this.iconTooltip
30190 initEvents: function()
30192 Roo.bootstrap.Element.superclass.initEvents.call(this);
30194 this.indicator = this.indicatorEl();
30196 if(this.indicator){
30197 this.indicator.removeClass('visible');
30198 this.indicator.addClass('invisible');
30201 Roo.bootstrap.FieldLabel.register(this);
30204 indicatorEl : function()
30206 var indicator = this.el.select('i.roo-required-indicator',true).first();
30217 * Mark this field as valid
30219 markValid : function()
30221 if(this.indicator){
30222 this.indicator.removeClass('visible');
30223 this.indicator.addClass('invisible');
30226 this.el.removeClass(this.invalidClass);
30228 this.el.addClass(this.validClass);
30230 this.fireEvent('valid', this);
30234 * Mark this field as invalid
30235 * @param {String} msg The validation message
30237 markInvalid : function(msg)
30239 if(this.indicator){
30240 this.indicator.removeClass('invisible');
30241 this.indicator.addClass('visible');
30244 this.el.removeClass(this.validClass);
30246 this.el.addClass(this.invalidClass);
30248 this.fireEvent('invalid', this, msg);
30254 Roo.apply(Roo.bootstrap.FieldLabel, {
30259 * register a FieldLabel Group
30260 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30262 register : function(label)
30264 if(this.groups.hasOwnProperty(label.target)){
30268 this.groups[label.target] = label;
30272 * fetch a FieldLabel Group based on the target
30273 * @param {string} target
30274 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30276 get: function(target) {
30277 if (typeof(this.groups[target]) == 'undefined') {
30281 return this.groups[target] ;
30290 * page DateSplitField.
30296 * @class Roo.bootstrap.DateSplitField
30297 * @extends Roo.bootstrap.Component
30298 * Bootstrap DateSplitField class
30299 * @cfg {string} fieldLabel - the label associated
30300 * @cfg {Number} labelWidth set the width of label (0-12)
30301 * @cfg {String} labelAlign (top|left)
30302 * @cfg {Boolean} dayAllowBlank (true|false) default false
30303 * @cfg {Boolean} monthAllowBlank (true|false) default false
30304 * @cfg {Boolean} yearAllowBlank (true|false) default false
30305 * @cfg {string} dayPlaceholder
30306 * @cfg {string} monthPlaceholder
30307 * @cfg {string} yearPlaceholder
30308 * @cfg {string} dayFormat default 'd'
30309 * @cfg {string} monthFormat default 'm'
30310 * @cfg {string} yearFormat default 'Y'
30311 * @cfg {Number} labellg set the width of label (1-12)
30312 * @cfg {Number} labelmd set the width of label (1-12)
30313 * @cfg {Number} labelsm set the width of label (1-12)
30314 * @cfg {Number} labelxs set the width of label (1-12)
30318 * Create a new DateSplitField
30319 * @param {Object} config The config object
30322 Roo.bootstrap.DateSplitField = function(config){
30323 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30329 * getting the data of years
30330 * @param {Roo.bootstrap.DateSplitField} this
30331 * @param {Object} years
30336 * getting the data of days
30337 * @param {Roo.bootstrap.DateSplitField} this
30338 * @param {Object} days
30343 * Fires after the field has been marked as invalid.
30344 * @param {Roo.form.Field} this
30345 * @param {String} msg The validation message
30350 * Fires after the field has been validated with no errors.
30351 * @param {Roo.form.Field} this
30357 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30360 labelAlign : 'top',
30362 dayAllowBlank : false,
30363 monthAllowBlank : false,
30364 yearAllowBlank : false,
30365 dayPlaceholder : '',
30366 monthPlaceholder : '',
30367 yearPlaceholder : '',
30371 isFormField : true,
30377 getAutoCreate : function()
30381 cls : 'row roo-date-split-field-group',
30386 cls : 'form-hidden-field roo-date-split-field-group-value',
30392 var labelCls = 'col-md-12';
30393 var contentCls = 'col-md-4';
30395 if(this.fieldLabel){
30399 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30403 html : this.fieldLabel
30408 if(this.labelAlign == 'left'){
30410 if(this.labelWidth > 12){
30411 label.style = "width: " + this.labelWidth + 'px';
30414 if(this.labelWidth < 13 && this.labelmd == 0){
30415 this.labelmd = this.labelWidth;
30418 if(this.labellg > 0){
30419 labelCls = ' col-lg-' + this.labellg;
30420 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30423 if(this.labelmd > 0){
30424 labelCls = ' col-md-' + this.labelmd;
30425 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30428 if(this.labelsm > 0){
30429 labelCls = ' col-sm-' + this.labelsm;
30430 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30433 if(this.labelxs > 0){
30434 labelCls = ' col-xs-' + this.labelxs;
30435 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30439 label.cls += ' ' + labelCls;
30441 cfg.cn.push(label);
30444 Roo.each(['day', 'month', 'year'], function(t){
30447 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30454 inputEl: function ()
30456 return this.el.select('.roo-date-split-field-group-value', true).first();
30459 onRender : function(ct, position)
30463 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30465 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30467 this.dayField = new Roo.bootstrap.ComboBox({
30468 allowBlank : this.dayAllowBlank,
30469 alwaysQuery : true,
30470 displayField : 'value',
30473 forceSelection : true,
30475 placeholder : this.dayPlaceholder,
30476 selectOnFocus : true,
30477 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30478 triggerAction : 'all',
30480 valueField : 'value',
30481 store : new Roo.data.SimpleStore({
30482 data : (function() {
30484 _this.fireEvent('days', _this, days);
30487 fields : [ 'value' ]
30490 select : function (_self, record, index)
30492 _this.setValue(_this.getValue());
30497 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30499 this.monthField = new Roo.bootstrap.MonthField({
30500 after : '<i class=\"fa fa-calendar\"></i>',
30501 allowBlank : this.monthAllowBlank,
30502 placeholder : this.monthPlaceholder,
30505 render : function (_self)
30507 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30508 e.preventDefault();
30512 select : function (_self, oldvalue, newvalue)
30514 _this.setValue(_this.getValue());
30519 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30521 this.yearField = new Roo.bootstrap.ComboBox({
30522 allowBlank : this.yearAllowBlank,
30523 alwaysQuery : true,
30524 displayField : 'value',
30527 forceSelection : true,
30529 placeholder : this.yearPlaceholder,
30530 selectOnFocus : true,
30531 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30532 triggerAction : 'all',
30534 valueField : 'value',
30535 store : new Roo.data.SimpleStore({
30536 data : (function() {
30538 _this.fireEvent('years', _this, years);
30541 fields : [ 'value' ]
30544 select : function (_self, record, index)
30546 _this.setValue(_this.getValue());
30551 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30554 setValue : function(v, format)
30556 this.inputEl.dom.value = v;
30558 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30560 var d = Date.parseDate(v, f);
30567 this.setDay(d.format(this.dayFormat));
30568 this.setMonth(d.format(this.monthFormat));
30569 this.setYear(d.format(this.yearFormat));
30576 setDay : function(v)
30578 this.dayField.setValue(v);
30579 this.inputEl.dom.value = this.getValue();
30584 setMonth : function(v)
30586 this.monthField.setValue(v, true);
30587 this.inputEl.dom.value = this.getValue();
30592 setYear : function(v)
30594 this.yearField.setValue(v);
30595 this.inputEl.dom.value = this.getValue();
30600 getDay : function()
30602 return this.dayField.getValue();
30605 getMonth : function()
30607 return this.monthField.getValue();
30610 getYear : function()
30612 return this.yearField.getValue();
30615 getValue : function()
30617 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30619 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30629 this.inputEl.dom.value = '';
30634 validate : function()
30636 var d = this.dayField.validate();
30637 var m = this.monthField.validate();
30638 var y = this.yearField.validate();
30643 (!this.dayAllowBlank && !d) ||
30644 (!this.monthAllowBlank && !m) ||
30645 (!this.yearAllowBlank && !y)
30650 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30659 this.markInvalid();
30664 markValid : function()
30667 var label = this.el.select('label', true).first();
30668 var icon = this.el.select('i.fa-star', true).first();
30674 this.fireEvent('valid', this);
30678 * Mark this field as invalid
30679 * @param {String} msg The validation message
30681 markInvalid : function(msg)
30684 var label = this.el.select('label', true).first();
30685 var icon = this.el.select('i.fa-star', true).first();
30687 if(label && !icon){
30688 this.el.select('.roo-date-split-field-label', true).createChild({
30690 cls : 'text-danger fa fa-lg fa-star',
30691 tooltip : 'This field is required',
30692 style : 'margin-right:5px;'
30696 this.fireEvent('invalid', this, msg);
30699 clearInvalid : function()
30701 var label = this.el.select('label', true).first();
30702 var icon = this.el.select('i.fa-star', true).first();
30708 this.fireEvent('valid', this);
30711 getName: function()
30721 * http://masonry.desandro.com
30723 * The idea is to render all the bricks based on vertical width...
30725 * The original code extends 'outlayer' - we might need to use that....
30731 * @class Roo.bootstrap.LayoutMasonry
30732 * @extends Roo.bootstrap.Component
30733 * Bootstrap Layout Masonry class
30736 * Create a new Element
30737 * @param {Object} config The config object
30740 Roo.bootstrap.LayoutMasonry = function(config){
30742 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30746 Roo.bootstrap.LayoutMasonry.register(this);
30752 * Fire after layout the items
30753 * @param {Roo.bootstrap.LayoutMasonry} this
30754 * @param {Roo.EventObject} e
30761 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30764 * @cfg {Boolean} isLayoutInstant = no animation?
30766 isLayoutInstant : false, // needed?
30769 * @cfg {Number} boxWidth width of the columns
30774 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30779 * @cfg {Number} padWidth padding below box..
30784 * @cfg {Number} gutter gutter width..
30789 * @cfg {Number} maxCols maximum number of columns
30795 * @cfg {Boolean} isAutoInitial defalut true
30797 isAutoInitial : true,
30802 * @cfg {Boolean} isHorizontal defalut false
30804 isHorizontal : false,
30806 currentSize : null,
30812 bricks: null, //CompositeElement
30816 _isLayoutInited : false,
30818 // isAlternative : false, // only use for vertical layout...
30821 * @cfg {Number} alternativePadWidth padding below box..
30823 alternativePadWidth : 50,
30825 selectedBrick : [],
30827 getAutoCreate : function(){
30829 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30833 cls: 'blog-masonary-wrapper ' + this.cls,
30835 cls : 'mas-boxes masonary'
30842 getChildContainer: function( )
30844 if (this.boxesEl) {
30845 return this.boxesEl;
30848 this.boxesEl = this.el.select('.mas-boxes').first();
30850 return this.boxesEl;
30854 initEvents : function()
30858 if(this.isAutoInitial){
30859 Roo.log('hook children rendered');
30860 this.on('childrenrendered', function() {
30861 Roo.log('children rendered');
30867 initial : function()
30869 this.selectedBrick = [];
30871 this.currentSize = this.el.getBox(true);
30873 Roo.EventManager.onWindowResize(this.resize, this);
30875 if(!this.isAutoInitial){
30883 //this.layout.defer(500,this);
30887 resize : function()
30889 var cs = this.el.getBox(true);
30892 this.currentSize.width == cs.width &&
30893 this.currentSize.x == cs.x &&
30894 this.currentSize.height == cs.height &&
30895 this.currentSize.y == cs.y
30897 Roo.log("no change in with or X or Y");
30901 this.currentSize = cs;
30907 layout : function()
30909 this._resetLayout();
30911 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30913 this.layoutItems( isInstant );
30915 this._isLayoutInited = true;
30917 this.fireEvent('layout', this);
30921 _resetLayout : function()
30923 if(this.isHorizontal){
30924 this.horizontalMeasureColumns();
30928 this.verticalMeasureColumns();
30932 verticalMeasureColumns : function()
30934 this.getContainerWidth();
30936 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30937 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30941 var boxWidth = this.boxWidth + this.padWidth;
30943 if(this.containerWidth < this.boxWidth){
30944 boxWidth = this.containerWidth
30947 var containerWidth = this.containerWidth;
30949 var cols = Math.floor(containerWidth / boxWidth);
30951 this.cols = Math.max( cols, 1 );
30953 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30955 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30957 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30959 this.colWidth = boxWidth + avail - this.padWidth;
30961 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30962 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30965 horizontalMeasureColumns : function()
30967 this.getContainerWidth();
30969 var boxWidth = this.boxWidth;
30971 if(this.containerWidth < boxWidth){
30972 boxWidth = this.containerWidth;
30975 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30977 this.el.setHeight(boxWidth);
30981 getContainerWidth : function()
30983 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30986 layoutItems : function( isInstant )
30988 Roo.log(this.bricks);
30990 var items = Roo.apply([], this.bricks);
30992 if(this.isHorizontal){
30993 this._horizontalLayoutItems( items , isInstant );
30997 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30998 // this._verticalAlternativeLayoutItems( items , isInstant );
31002 this._verticalLayoutItems( items , isInstant );
31006 _verticalLayoutItems : function ( items , isInstant)
31008 if ( !items || !items.length ) {
31013 ['xs', 'xs', 'xs', 'tall'],
31014 ['xs', 'xs', 'tall'],
31015 ['xs', 'xs', 'sm'],
31016 ['xs', 'xs', 'xs'],
31022 ['sm', 'xs', 'xs'],
31026 ['tall', 'xs', 'xs', 'xs'],
31027 ['tall', 'xs', 'xs'],
31039 Roo.each(items, function(item, k){
31041 switch (item.size) {
31042 // these layouts take up a full box,
31053 boxes.push([item]);
31076 var filterPattern = function(box, length)
31084 var pattern = box.slice(0, length);
31088 Roo.each(pattern, function(i){
31089 format.push(i.size);
31092 Roo.each(standard, function(s){
31094 if(String(s) != String(format)){
31103 if(!match && length == 1){
31108 filterPattern(box, length - 1);
31112 queue.push(pattern);
31114 box = box.slice(length, box.length);
31116 filterPattern(box, 4);
31122 Roo.each(boxes, function(box, k){
31128 if(box.length == 1){
31133 filterPattern(box, 4);
31137 this._processVerticalLayoutQueue( queue, isInstant );
31141 // _verticalAlternativeLayoutItems : function( items , isInstant )
31143 // if ( !items || !items.length ) {
31147 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31151 _horizontalLayoutItems : function ( items , isInstant)
31153 if ( !items || !items.length || items.length < 3) {
31159 var eItems = items.slice(0, 3);
31161 items = items.slice(3, items.length);
31164 ['xs', 'xs', 'xs', 'wide'],
31165 ['xs', 'xs', 'wide'],
31166 ['xs', 'xs', 'sm'],
31167 ['xs', 'xs', 'xs'],
31173 ['sm', 'xs', 'xs'],
31177 ['wide', 'xs', 'xs', 'xs'],
31178 ['wide', 'xs', 'xs'],
31191 Roo.each(items, function(item, k){
31193 switch (item.size) {
31204 boxes.push([item]);
31228 var filterPattern = function(box, length)
31236 var pattern = box.slice(0, length);
31240 Roo.each(pattern, function(i){
31241 format.push(i.size);
31244 Roo.each(standard, function(s){
31246 if(String(s) != String(format)){
31255 if(!match && length == 1){
31260 filterPattern(box, length - 1);
31264 queue.push(pattern);
31266 box = box.slice(length, box.length);
31268 filterPattern(box, 4);
31274 Roo.each(boxes, function(box, k){
31280 if(box.length == 1){
31285 filterPattern(box, 4);
31292 var pos = this.el.getBox(true);
31296 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31298 var hit_end = false;
31300 Roo.each(queue, function(box){
31304 Roo.each(box, function(b){
31306 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31316 Roo.each(box, function(b){
31318 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31321 mx = Math.max(mx, b.x);
31325 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31329 Roo.each(box, function(b){
31331 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31345 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31348 /** Sets position of item in DOM
31349 * @param {Element} item
31350 * @param {Number} x - horizontal position
31351 * @param {Number} y - vertical position
31352 * @param {Boolean} isInstant - disables transitions
31354 _processVerticalLayoutQueue : function( queue, isInstant )
31356 var pos = this.el.getBox(true);
31361 for (var i = 0; i < this.cols; i++){
31365 Roo.each(queue, function(box, k){
31367 var col = k % this.cols;
31369 Roo.each(box, function(b,kk){
31371 b.el.position('absolute');
31373 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31374 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31376 if(b.size == 'md-left' || b.size == 'md-right'){
31377 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31378 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31381 b.el.setWidth(width);
31382 b.el.setHeight(height);
31384 b.el.select('iframe',true).setSize(width,height);
31388 for (var i = 0; i < this.cols; i++){
31390 if(maxY[i] < maxY[col]){
31395 col = Math.min(col, i);
31399 x = pos.x + col * (this.colWidth + this.padWidth);
31403 var positions = [];
31405 switch (box.length){
31407 positions = this.getVerticalOneBoxColPositions(x, y, box);
31410 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31413 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31416 positions = this.getVerticalFourBoxColPositions(x, y, box);
31422 Roo.each(box, function(b,kk){
31424 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31426 var sz = b.el.getSize();
31428 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31436 for (var i = 0; i < this.cols; i++){
31437 mY = Math.max(mY, maxY[i]);
31440 this.el.setHeight(mY - pos.y);
31444 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31446 // var pos = this.el.getBox(true);
31449 // var maxX = pos.right;
31451 // var maxHeight = 0;
31453 // Roo.each(items, function(item, k){
31457 // item.el.position('absolute');
31459 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31461 // item.el.setWidth(width);
31463 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31465 // item.el.setHeight(height);
31468 // item.el.setXY([x, y], isInstant ? false : true);
31470 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31473 // y = y + height + this.alternativePadWidth;
31475 // maxHeight = maxHeight + height + this.alternativePadWidth;
31479 // this.el.setHeight(maxHeight);
31483 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31485 var pos = this.el.getBox(true);
31490 var maxX = pos.right;
31492 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31494 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31496 Roo.each(queue, function(box, k){
31498 Roo.each(box, function(b, kk){
31500 b.el.position('absolute');
31502 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31503 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31505 if(b.size == 'md-left' || b.size == 'md-right'){
31506 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31507 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31510 b.el.setWidth(width);
31511 b.el.setHeight(height);
31519 var positions = [];
31521 switch (box.length){
31523 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31526 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31529 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31532 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31538 Roo.each(box, function(b,kk){
31540 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31542 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31550 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31552 Roo.each(eItems, function(b,k){
31554 b.size = (k == 0) ? 'sm' : 'xs';
31555 b.x = (k == 0) ? 2 : 1;
31556 b.y = (k == 0) ? 2 : 1;
31558 b.el.position('absolute');
31560 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31562 b.el.setWidth(width);
31564 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31566 b.el.setHeight(height);
31570 var positions = [];
31573 x : maxX - this.unitWidth * 2 - this.gutter,
31578 x : maxX - this.unitWidth,
31579 y : minY + (this.unitWidth + this.gutter) * 2
31583 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31587 Roo.each(eItems, function(b,k){
31589 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31595 getVerticalOneBoxColPositions : function(x, y, box)
31599 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31601 if(box[0].size == 'md-left'){
31605 if(box[0].size == 'md-right'){
31610 x : x + (this.unitWidth + this.gutter) * rand,
31617 getVerticalTwoBoxColPositions : function(x, y, box)
31621 if(box[0].size == 'xs'){
31625 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31629 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31643 x : x + (this.unitWidth + this.gutter) * 2,
31644 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31651 getVerticalThreeBoxColPositions : function(x, y, box)
31655 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31663 x : x + (this.unitWidth + this.gutter) * 1,
31668 x : x + (this.unitWidth + this.gutter) * 2,
31676 if(box[0].size == 'xs' && box[1].size == 'xs'){
31685 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31689 x : x + (this.unitWidth + this.gutter) * 1,
31703 x : x + (this.unitWidth + this.gutter) * 2,
31708 x : x + (this.unitWidth + this.gutter) * 2,
31709 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31716 getVerticalFourBoxColPositions : function(x, y, box)
31720 if(box[0].size == 'xs'){
31729 y : y + (this.unitHeight + this.gutter) * 1
31734 y : y + (this.unitHeight + this.gutter) * 2
31738 x : x + (this.unitWidth + this.gutter) * 1,
31752 x : x + (this.unitWidth + this.gutter) * 2,
31757 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31758 y : y + (this.unitHeight + this.gutter) * 1
31762 x : x + (this.unitWidth + this.gutter) * 2,
31763 y : y + (this.unitWidth + this.gutter) * 2
31770 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31774 if(box[0].size == 'md-left'){
31776 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31783 if(box[0].size == 'md-right'){
31785 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31786 y : minY + (this.unitWidth + this.gutter) * 1
31792 var rand = Math.floor(Math.random() * (4 - box[0].y));
31795 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31796 y : minY + (this.unitWidth + this.gutter) * rand
31803 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31807 if(box[0].size == 'xs'){
31810 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31815 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31816 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31824 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31829 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31830 y : minY + (this.unitWidth + this.gutter) * 2
31837 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31841 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31844 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31849 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31850 y : minY + (this.unitWidth + this.gutter) * 1
31854 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31855 y : minY + (this.unitWidth + this.gutter) * 2
31862 if(box[0].size == 'xs' && box[1].size == 'xs'){
31865 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31870 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31875 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31876 y : minY + (this.unitWidth + this.gutter) * 1
31884 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31889 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31890 y : minY + (this.unitWidth + this.gutter) * 2
31894 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31895 y : minY + (this.unitWidth + this.gutter) * 2
31902 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31906 if(box[0].size == 'xs'){
31909 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31914 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31919 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),
31924 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31925 y : minY + (this.unitWidth + this.gutter) * 1
31933 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31938 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31939 y : minY + (this.unitWidth + this.gutter) * 2
31943 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31944 y : minY + (this.unitWidth + this.gutter) * 2
31948 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),
31949 y : minY + (this.unitWidth + this.gutter) * 2
31957 * remove a Masonry Brick
31958 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31960 removeBrick : function(brick_id)
31966 for (var i = 0; i<this.bricks.length; i++) {
31967 if (this.bricks[i].id == brick_id) {
31968 this.bricks.splice(i,1);
31969 this.el.dom.removeChild(Roo.get(brick_id).dom);
31976 * adds a Masonry Brick
31977 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31979 addBrick : function(cfg)
31981 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31982 //this.register(cn);
31983 cn.parentId = this.id;
31984 cn.onRender(this.el, null);
31989 * register a Masonry Brick
31990 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31993 register : function(brick)
31995 this.bricks.push(brick);
31996 brick.masonryId = this.id;
32000 * clear all the Masonry Brick
32002 clearAll : function()
32005 //this.getChildContainer().dom.innerHTML = "";
32006 this.el.dom.innerHTML = '';
32009 getSelected : function()
32011 if (!this.selectedBrick) {
32015 return this.selectedBrick;
32019 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32023 * register a Masonry Layout
32024 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32027 register : function(layout)
32029 this.groups[layout.id] = layout;
32032 * fetch a Masonry Layout based on the masonry layout ID
32033 * @param {string} the masonry layout to add
32034 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32037 get: function(layout_id) {
32038 if (typeof(this.groups[layout_id]) == 'undefined') {
32041 return this.groups[layout_id] ;
32053 * http://masonry.desandro.com
32055 * The idea is to render all the bricks based on vertical width...
32057 * The original code extends 'outlayer' - we might need to use that....
32063 * @class Roo.bootstrap.LayoutMasonryAuto
32064 * @extends Roo.bootstrap.Component
32065 * Bootstrap Layout Masonry class
32068 * Create a new Element
32069 * @param {Object} config The config object
32072 Roo.bootstrap.LayoutMasonryAuto = function(config){
32073 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32076 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32079 * @cfg {Boolean} isFitWidth - resize the width..
32081 isFitWidth : false, // options..
32083 * @cfg {Boolean} isOriginLeft = left align?
32085 isOriginLeft : true,
32087 * @cfg {Boolean} isOriginTop = top align?
32089 isOriginTop : false,
32091 * @cfg {Boolean} isLayoutInstant = no animation?
32093 isLayoutInstant : false, // needed?
32095 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32097 isResizingContainer : true,
32099 * @cfg {Number} columnWidth width of the columns
32105 * @cfg {Number} maxCols maximum number of columns
32110 * @cfg {Number} padHeight padding below box..
32116 * @cfg {Boolean} isAutoInitial defalut true
32119 isAutoInitial : true,
32125 initialColumnWidth : 0,
32126 currentSize : null,
32128 colYs : null, // array.
32135 bricks: null, //CompositeElement
32136 cols : 0, // array?
32137 // element : null, // wrapped now this.el
32138 _isLayoutInited : null,
32141 getAutoCreate : function(){
32145 cls: 'blog-masonary-wrapper ' + this.cls,
32147 cls : 'mas-boxes masonary'
32154 getChildContainer: function( )
32156 if (this.boxesEl) {
32157 return this.boxesEl;
32160 this.boxesEl = this.el.select('.mas-boxes').first();
32162 return this.boxesEl;
32166 initEvents : function()
32170 if(this.isAutoInitial){
32171 Roo.log('hook children rendered');
32172 this.on('childrenrendered', function() {
32173 Roo.log('children rendered');
32180 initial : function()
32182 this.reloadItems();
32184 this.currentSize = this.el.getBox(true);
32186 /// was window resize... - let's see if this works..
32187 Roo.EventManager.onWindowResize(this.resize, this);
32189 if(!this.isAutoInitial){
32194 this.layout.defer(500,this);
32197 reloadItems: function()
32199 this.bricks = this.el.select('.masonry-brick', true);
32201 this.bricks.each(function(b) {
32202 //Roo.log(b.getSize());
32203 if (!b.attr('originalwidth')) {
32204 b.attr('originalwidth', b.getSize().width);
32209 Roo.log(this.bricks.elements.length);
32212 resize : function()
32215 var cs = this.el.getBox(true);
32217 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32218 Roo.log("no change in with or X");
32221 this.currentSize = cs;
32225 layout : function()
32228 this._resetLayout();
32229 //this._manageStamps();
32231 // don't animate first layout
32232 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32233 this.layoutItems( isInstant );
32235 // flag for initalized
32236 this._isLayoutInited = true;
32239 layoutItems : function( isInstant )
32241 //var items = this._getItemsForLayout( this.items );
32242 // original code supports filtering layout items.. we just ignore it..
32244 this._layoutItems( this.bricks , isInstant );
32246 this._postLayout();
32248 _layoutItems : function ( items , isInstant)
32250 //this.fireEvent( 'layout', this, items );
32253 if ( !items || !items.elements.length ) {
32254 // no items, emit event with empty array
32259 items.each(function(item) {
32260 Roo.log("layout item");
32262 // get x/y object from method
32263 var position = this._getItemLayoutPosition( item );
32265 position.item = item;
32266 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32267 queue.push( position );
32270 this._processLayoutQueue( queue );
32272 /** Sets position of item in DOM
32273 * @param {Element} item
32274 * @param {Number} x - horizontal position
32275 * @param {Number} y - vertical position
32276 * @param {Boolean} isInstant - disables transitions
32278 _processLayoutQueue : function( queue )
32280 for ( var i=0, len = queue.length; i < len; i++ ) {
32281 var obj = queue[i];
32282 obj.item.position('absolute');
32283 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32289 * Any logic you want to do after each layout,
32290 * i.e. size the container
32292 _postLayout : function()
32294 this.resizeContainer();
32297 resizeContainer : function()
32299 if ( !this.isResizingContainer ) {
32302 var size = this._getContainerSize();
32304 this.el.setSize(size.width,size.height);
32305 this.boxesEl.setSize(size.width,size.height);
32311 _resetLayout : function()
32313 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32314 this.colWidth = this.el.getWidth();
32315 //this.gutter = this.el.getWidth();
32317 this.measureColumns();
32323 this.colYs.push( 0 );
32329 measureColumns : function()
32331 this.getContainerWidth();
32332 // if columnWidth is 0, default to outerWidth of first item
32333 if ( !this.columnWidth ) {
32334 var firstItem = this.bricks.first();
32335 Roo.log(firstItem);
32336 this.columnWidth = this.containerWidth;
32337 if (firstItem && firstItem.attr('originalwidth') ) {
32338 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32340 // columnWidth fall back to item of first element
32341 Roo.log("set column width?");
32342 this.initialColumnWidth = this.columnWidth ;
32344 // if first elem has no width, default to size of container
32349 if (this.initialColumnWidth) {
32350 this.columnWidth = this.initialColumnWidth;
32355 // column width is fixed at the top - however if container width get's smaller we should
32358 // this bit calcs how man columns..
32360 var columnWidth = this.columnWidth += this.gutter;
32362 // calculate columns
32363 var containerWidth = this.containerWidth + this.gutter;
32365 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32366 // fix rounding errors, typically with gutters
32367 var excess = columnWidth - containerWidth % columnWidth;
32370 // if overshoot is less than a pixel, round up, otherwise floor it
32371 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32372 cols = Math[ mathMethod ]( cols );
32373 this.cols = Math.max( cols, 1 );
32374 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32376 // padding positioning..
32377 var totalColWidth = this.cols * this.columnWidth;
32378 var padavail = this.containerWidth - totalColWidth;
32379 // so for 2 columns - we need 3 'pads'
32381 var padNeeded = (1+this.cols) * this.padWidth;
32383 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32385 this.columnWidth += padExtra
32386 //this.padWidth = Math.floor(padavail / ( this.cols));
32388 // adjust colum width so that padding is fixed??
32390 // we have 3 columns ... total = width * 3
32391 // we have X left over... that should be used by
32393 //if (this.expandC) {
32401 getContainerWidth : function()
32403 /* // container is parent if fit width
32404 var container = this.isFitWidth ? this.element.parentNode : this.element;
32405 // check that this.size and size are there
32406 // IE8 triggers resize on body size change, so they might not be
32408 var size = getSize( container ); //FIXME
32409 this.containerWidth = size && size.innerWidth; //FIXME
32412 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32416 _getItemLayoutPosition : function( item ) // what is item?
32418 // we resize the item to our columnWidth..
32420 item.setWidth(this.columnWidth);
32421 item.autoBoxAdjust = false;
32423 var sz = item.getSize();
32425 // how many columns does this brick span
32426 var remainder = this.containerWidth % this.columnWidth;
32428 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32429 // round if off by 1 pixel, otherwise use ceil
32430 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32431 colSpan = Math.min( colSpan, this.cols );
32433 // normally this should be '1' as we dont' currently allow multi width columns..
32435 var colGroup = this._getColGroup( colSpan );
32436 // get the minimum Y value from the columns
32437 var minimumY = Math.min.apply( Math, colGroup );
32438 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32440 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32442 // position the brick
32444 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32445 y: this.currentSize.y + minimumY + this.padHeight
32449 // apply setHeight to necessary columns
32450 var setHeight = minimumY + sz.height + this.padHeight;
32451 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32453 var setSpan = this.cols + 1 - colGroup.length;
32454 for ( var i = 0; i < setSpan; i++ ) {
32455 this.colYs[ shortColIndex + i ] = setHeight ;
32462 * @param {Number} colSpan - number of columns the element spans
32463 * @returns {Array} colGroup
32465 _getColGroup : function( colSpan )
32467 if ( colSpan < 2 ) {
32468 // if brick spans only one column, use all the column Ys
32473 // how many different places could this brick fit horizontally
32474 var groupCount = this.cols + 1 - colSpan;
32475 // for each group potential horizontal position
32476 for ( var i = 0; i < groupCount; i++ ) {
32477 // make an array of colY values for that one group
32478 var groupColYs = this.colYs.slice( i, i + colSpan );
32479 // and get the max value of the array
32480 colGroup[i] = Math.max.apply( Math, groupColYs );
32485 _manageStamp : function( stamp )
32487 var stampSize = stamp.getSize();
32488 var offset = stamp.getBox();
32489 // get the columns that this stamp affects
32490 var firstX = this.isOriginLeft ? offset.x : offset.right;
32491 var lastX = firstX + stampSize.width;
32492 var firstCol = Math.floor( firstX / this.columnWidth );
32493 firstCol = Math.max( 0, firstCol );
32495 var lastCol = Math.floor( lastX / this.columnWidth );
32496 // lastCol should not go over if multiple of columnWidth #425
32497 lastCol -= lastX % this.columnWidth ? 0 : 1;
32498 lastCol = Math.min( this.cols - 1, lastCol );
32500 // set colYs to bottom of the stamp
32501 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32504 for ( var i = firstCol; i <= lastCol; i++ ) {
32505 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32510 _getContainerSize : function()
32512 this.maxY = Math.max.apply( Math, this.colYs );
32517 if ( this.isFitWidth ) {
32518 size.width = this._getContainerFitWidth();
32524 _getContainerFitWidth : function()
32526 var unusedCols = 0;
32527 // count unused columns
32530 if ( this.colYs[i] !== 0 ) {
32535 // fit container to columns that have been used
32536 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32539 needsResizeLayout : function()
32541 var previousWidth = this.containerWidth;
32542 this.getContainerWidth();
32543 return previousWidth !== this.containerWidth;
32558 * @class Roo.bootstrap.MasonryBrick
32559 * @extends Roo.bootstrap.Component
32560 * Bootstrap MasonryBrick class
32563 * Create a new MasonryBrick
32564 * @param {Object} config The config object
32567 Roo.bootstrap.MasonryBrick = function(config){
32569 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32571 Roo.bootstrap.MasonryBrick.register(this);
32577 * When a MasonryBrick is clcik
32578 * @param {Roo.bootstrap.MasonryBrick} this
32579 * @param {Roo.EventObject} e
32585 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32588 * @cfg {String} title
32592 * @cfg {String} html
32596 * @cfg {String} bgimage
32600 * @cfg {String} videourl
32604 * @cfg {String} cls
32608 * @cfg {String} href
32612 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32617 * @cfg {String} placetitle (center|bottom)
32622 * @cfg {Boolean} isFitContainer defalut true
32624 isFitContainer : true,
32627 * @cfg {Boolean} preventDefault defalut false
32629 preventDefault : false,
32632 * @cfg {Boolean} inverse defalut false
32634 maskInverse : false,
32636 getAutoCreate : function()
32638 if(!this.isFitContainer){
32639 return this.getSplitAutoCreate();
32642 var cls = 'masonry-brick masonry-brick-full';
32644 if(this.href.length){
32645 cls += ' masonry-brick-link';
32648 if(this.bgimage.length){
32649 cls += ' masonry-brick-image';
32652 if(this.maskInverse){
32653 cls += ' mask-inverse';
32656 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32657 cls += ' enable-mask';
32661 cls += ' masonry-' + this.size + '-brick';
32664 if(this.placetitle.length){
32666 switch (this.placetitle) {
32668 cls += ' masonry-center-title';
32671 cls += ' masonry-bottom-title';
32678 if(!this.html.length && !this.bgimage.length){
32679 cls += ' masonry-center-title';
32682 if(!this.html.length && this.bgimage.length){
32683 cls += ' masonry-bottom-title';
32688 cls += ' ' + this.cls;
32692 tag: (this.href.length) ? 'a' : 'div',
32697 cls: 'masonry-brick-mask'
32701 cls: 'masonry-brick-paragraph',
32707 if(this.href.length){
32708 cfg.href = this.href;
32711 var cn = cfg.cn[1].cn;
32713 if(this.title.length){
32716 cls: 'masonry-brick-title',
32721 if(this.html.length){
32724 cls: 'masonry-brick-text',
32729 if (!this.title.length && !this.html.length) {
32730 cfg.cn[1].cls += ' hide';
32733 if(this.bgimage.length){
32736 cls: 'masonry-brick-image-view',
32741 if(this.videourl.length){
32742 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32743 // youtube support only?
32746 cls: 'masonry-brick-image-view',
32749 allowfullscreen : true
32757 getSplitAutoCreate : function()
32759 var cls = 'masonry-brick masonry-brick-split';
32761 if(this.href.length){
32762 cls += ' masonry-brick-link';
32765 if(this.bgimage.length){
32766 cls += ' masonry-brick-image';
32770 cls += ' masonry-' + this.size + '-brick';
32773 switch (this.placetitle) {
32775 cls += ' masonry-center-title';
32778 cls += ' masonry-bottom-title';
32781 if(!this.bgimage.length){
32782 cls += ' masonry-center-title';
32785 if(this.bgimage.length){
32786 cls += ' masonry-bottom-title';
32792 cls += ' ' + this.cls;
32796 tag: (this.href.length) ? 'a' : 'div',
32801 cls: 'masonry-brick-split-head',
32805 cls: 'masonry-brick-paragraph',
32812 cls: 'masonry-brick-split-body',
32818 if(this.href.length){
32819 cfg.href = this.href;
32822 if(this.title.length){
32823 cfg.cn[0].cn[0].cn.push({
32825 cls: 'masonry-brick-title',
32830 if(this.html.length){
32831 cfg.cn[1].cn.push({
32833 cls: 'masonry-brick-text',
32838 if(this.bgimage.length){
32839 cfg.cn[0].cn.push({
32841 cls: 'masonry-brick-image-view',
32846 if(this.videourl.length){
32847 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32848 // youtube support only?
32849 cfg.cn[0].cn.cn.push({
32851 cls: 'masonry-brick-image-view',
32854 allowfullscreen : true
32861 initEvents: function()
32863 switch (this.size) {
32896 this.el.on('touchstart', this.onTouchStart, this);
32897 this.el.on('touchmove', this.onTouchMove, this);
32898 this.el.on('touchend', this.onTouchEnd, this);
32899 this.el.on('contextmenu', this.onContextMenu, this);
32901 this.el.on('mouseenter' ,this.enter, this);
32902 this.el.on('mouseleave', this.leave, this);
32903 this.el.on('click', this.onClick, this);
32906 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32907 this.parent().bricks.push(this);
32912 onClick: function(e, el)
32914 var time = this.endTimer - this.startTimer;
32915 // Roo.log(e.preventDefault());
32918 e.preventDefault();
32923 if(!this.preventDefault){
32927 e.preventDefault();
32929 if (this.activeClass != '') {
32930 this.selectBrick();
32933 this.fireEvent('click', this, e);
32936 enter: function(e, el)
32938 e.preventDefault();
32940 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32944 if(this.bgimage.length && this.html.length){
32945 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32949 leave: function(e, el)
32951 e.preventDefault();
32953 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32957 if(this.bgimage.length && this.html.length){
32958 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32962 onTouchStart: function(e, el)
32964 // e.preventDefault();
32966 this.touchmoved = false;
32968 if(!this.isFitContainer){
32972 if(!this.bgimage.length || !this.html.length){
32976 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32978 this.timer = new Date().getTime();
32982 onTouchMove: function(e, el)
32984 this.touchmoved = true;
32987 onContextMenu : function(e,el)
32989 e.preventDefault();
32990 e.stopPropagation();
32994 onTouchEnd: function(e, el)
32996 // e.preventDefault();
32998 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33005 if(!this.bgimage.length || !this.html.length){
33007 if(this.href.length){
33008 window.location.href = this.href;
33014 if(!this.isFitContainer){
33018 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33020 window.location.href = this.href;
33023 //selection on single brick only
33024 selectBrick : function() {
33026 if (!this.parentId) {
33030 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33031 var index = m.selectedBrick.indexOf(this.id);
33034 m.selectedBrick.splice(index,1);
33035 this.el.removeClass(this.activeClass);
33039 for(var i = 0; i < m.selectedBrick.length; i++) {
33040 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33041 b.el.removeClass(b.activeClass);
33044 m.selectedBrick = [];
33046 m.selectedBrick.push(this.id);
33047 this.el.addClass(this.activeClass);
33051 isSelected : function(){
33052 return this.el.hasClass(this.activeClass);
33057 Roo.apply(Roo.bootstrap.MasonryBrick, {
33060 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33062 * register a Masonry Brick
33063 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33066 register : function(brick)
33068 //this.groups[brick.id] = brick;
33069 this.groups.add(brick.id, brick);
33072 * fetch a masonry brick based on the masonry brick ID
33073 * @param {string} the masonry brick to add
33074 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33077 get: function(brick_id)
33079 // if (typeof(this.groups[brick_id]) == 'undefined') {
33082 // return this.groups[brick_id] ;
33084 if(this.groups.key(brick_id)) {
33085 return this.groups.key(brick_id);
33103 * @class Roo.bootstrap.Brick
33104 * @extends Roo.bootstrap.Component
33105 * Bootstrap Brick class
33108 * Create a new Brick
33109 * @param {Object} config The config object
33112 Roo.bootstrap.Brick = function(config){
33113 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33119 * When a Brick is click
33120 * @param {Roo.bootstrap.Brick} this
33121 * @param {Roo.EventObject} e
33127 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33130 * @cfg {String} title
33134 * @cfg {String} html
33138 * @cfg {String} bgimage
33142 * @cfg {String} cls
33146 * @cfg {String} href
33150 * @cfg {String} video
33154 * @cfg {Boolean} square
33158 getAutoCreate : function()
33160 var cls = 'roo-brick';
33162 if(this.href.length){
33163 cls += ' roo-brick-link';
33166 if(this.bgimage.length){
33167 cls += ' roo-brick-image';
33170 if(!this.html.length && !this.bgimage.length){
33171 cls += ' roo-brick-center-title';
33174 if(!this.html.length && this.bgimage.length){
33175 cls += ' roo-brick-bottom-title';
33179 cls += ' ' + this.cls;
33183 tag: (this.href.length) ? 'a' : 'div',
33188 cls: 'roo-brick-paragraph',
33194 if(this.href.length){
33195 cfg.href = this.href;
33198 var cn = cfg.cn[0].cn;
33200 if(this.title.length){
33203 cls: 'roo-brick-title',
33208 if(this.html.length){
33211 cls: 'roo-brick-text',
33218 if(this.bgimage.length){
33221 cls: 'roo-brick-image-view',
33229 initEvents: function()
33231 if(this.title.length || this.html.length){
33232 this.el.on('mouseenter' ,this.enter, this);
33233 this.el.on('mouseleave', this.leave, this);
33236 Roo.EventManager.onWindowResize(this.resize, this);
33238 if(this.bgimage.length){
33239 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33240 this.imageEl.on('load', this.onImageLoad, this);
33247 onImageLoad : function()
33252 resize : function()
33254 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33256 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33258 if(this.bgimage.length){
33259 var image = this.el.select('.roo-brick-image-view', true).first();
33261 image.setWidth(paragraph.getWidth());
33264 image.setHeight(paragraph.getWidth());
33267 this.el.setHeight(image.getHeight());
33268 paragraph.setHeight(image.getHeight());
33274 enter: function(e, el)
33276 e.preventDefault();
33278 if(this.bgimage.length){
33279 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33280 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33284 leave: function(e, el)
33286 e.preventDefault();
33288 if(this.bgimage.length){
33289 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33290 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33305 * @class Roo.bootstrap.NumberField
33306 * @extends Roo.bootstrap.Input
33307 * Bootstrap NumberField class
33313 * Create a new NumberField
33314 * @param {Object} config The config object
33317 Roo.bootstrap.NumberField = function(config){
33318 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33321 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33324 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33326 allowDecimals : true,
33328 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33330 decimalSeparator : ".",
33332 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33334 decimalPrecision : 2,
33336 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33338 allowNegative : true,
33341 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33345 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33347 minValue : Number.NEGATIVE_INFINITY,
33349 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33351 maxValue : Number.MAX_VALUE,
33353 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33355 minText : "The minimum value for this field is {0}",
33357 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33359 maxText : "The maximum value for this field is {0}",
33361 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33362 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33364 nanText : "{0} is not a valid number",
33366 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33368 thousandsDelimiter : false,
33370 * @cfg {String} valueAlign alignment of value
33372 valueAlign : "left",
33374 getAutoCreate : function()
33376 var hiddenInput = {
33380 cls: 'hidden-number-input'
33384 hiddenInput.name = this.name;
33389 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33391 this.name = hiddenInput.name;
33393 if(cfg.cn.length > 0) {
33394 cfg.cn.push(hiddenInput);
33401 initEvents : function()
33403 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33405 var allowed = "0123456789";
33407 if(this.allowDecimals){
33408 allowed += this.decimalSeparator;
33411 if(this.allowNegative){
33415 if(this.thousandsDelimiter) {
33419 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33421 var keyPress = function(e){
33423 var k = e.getKey();
33425 var c = e.getCharCode();
33428 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33429 allowed.indexOf(String.fromCharCode(c)) === -1
33435 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33439 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33444 this.el.on("keypress", keyPress, this);
33447 validateValue : function(value)
33450 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33454 var num = this.parseValue(value);
33457 this.markInvalid(String.format(this.nanText, value));
33461 if(num < this.minValue){
33462 this.markInvalid(String.format(this.minText, this.minValue));
33466 if(num > this.maxValue){
33467 this.markInvalid(String.format(this.maxText, this.maxValue));
33474 getValue : function()
33476 var v = this.hiddenEl().getValue();
33478 return this.fixPrecision(this.parseValue(v));
33481 parseValue : function(value)
33483 if(this.thousandsDelimiter) {
33485 r = new RegExp(",", "g");
33486 value = value.replace(r, "");
33489 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33490 return isNaN(value) ? '' : value;
33493 fixPrecision : function(value)
33495 if(this.thousandsDelimiter) {
33497 r = new RegExp(",", "g");
33498 value = value.replace(r, "");
33501 var nan = isNaN(value);
33503 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33504 return nan ? '' : value;
33506 return parseFloat(value).toFixed(this.decimalPrecision);
33509 setValue : function(v)
33511 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33517 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33519 this.inputEl().dom.value = (v == '') ? '' :
33520 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33522 if(!this.allowZero && v === '0') {
33523 this.hiddenEl().dom.value = '';
33524 this.inputEl().dom.value = '';
33531 decimalPrecisionFcn : function(v)
33533 return Math.floor(v);
33536 beforeBlur : function()
33538 var v = this.parseValue(this.getRawValue());
33540 if(v || v === 0 || v === ''){
33545 hiddenEl : function()
33547 return this.el.select('input.hidden-number-input',true).first();
33559 * @class Roo.bootstrap.DocumentSlider
33560 * @extends Roo.bootstrap.Component
33561 * Bootstrap DocumentSlider class
33564 * Create a new DocumentViewer
33565 * @param {Object} config The config object
33568 Roo.bootstrap.DocumentSlider = function(config){
33569 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33576 * Fire after initEvent
33577 * @param {Roo.bootstrap.DocumentSlider} this
33582 * Fire after update
33583 * @param {Roo.bootstrap.DocumentSlider} this
33589 * @param {Roo.bootstrap.DocumentSlider} this
33595 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33601 getAutoCreate : function()
33605 cls : 'roo-document-slider',
33609 cls : 'roo-document-slider-header',
33613 cls : 'roo-document-slider-header-title'
33619 cls : 'roo-document-slider-body',
33623 cls : 'roo-document-slider-prev',
33627 cls : 'fa fa-chevron-left'
33633 cls : 'roo-document-slider-thumb',
33637 cls : 'roo-document-slider-image'
33643 cls : 'roo-document-slider-next',
33647 cls : 'fa fa-chevron-right'
33659 initEvents : function()
33661 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33662 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33664 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33665 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33667 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33668 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33670 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33671 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33673 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33674 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33676 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33677 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33679 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33680 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33682 this.thumbEl.on('click', this.onClick, this);
33684 this.prevIndicator.on('click', this.prev, this);
33686 this.nextIndicator.on('click', this.next, this);
33690 initial : function()
33692 if(this.files.length){
33693 this.indicator = 1;
33697 this.fireEvent('initial', this);
33700 update : function()
33702 this.imageEl.attr('src', this.files[this.indicator - 1]);
33704 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33706 this.prevIndicator.show();
33708 if(this.indicator == 1){
33709 this.prevIndicator.hide();
33712 this.nextIndicator.show();
33714 if(this.indicator == this.files.length){
33715 this.nextIndicator.hide();
33718 this.thumbEl.scrollTo('top');
33720 this.fireEvent('update', this);
33723 onClick : function(e)
33725 e.preventDefault();
33727 this.fireEvent('click', this);
33732 e.preventDefault();
33734 this.indicator = Math.max(1, this.indicator - 1);
33741 e.preventDefault();
33743 this.indicator = Math.min(this.files.length, this.indicator + 1);
33757 * @class Roo.bootstrap.RadioSet
33758 * @extends Roo.bootstrap.Input
33759 * Bootstrap RadioSet class
33760 * @cfg {String} indicatorpos (left|right) default left
33761 * @cfg {Boolean} inline (true|false) inline the element (default true)
33762 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33764 * Create a new RadioSet
33765 * @param {Object} config The config object
33768 Roo.bootstrap.RadioSet = function(config){
33770 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33774 Roo.bootstrap.RadioSet.register(this);
33779 * Fires when the element is checked or unchecked.
33780 * @param {Roo.bootstrap.RadioSet} this This radio
33781 * @param {Roo.bootstrap.Radio} item The checked item
33786 * Fires when the element is click.
33787 * @param {Roo.bootstrap.RadioSet} this This radio set
33788 * @param {Roo.bootstrap.Radio} item The checked item
33789 * @param {Roo.EventObject} e The event object
33796 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33804 indicatorpos : 'left',
33806 getAutoCreate : function()
33810 cls : 'roo-radio-set-label',
33814 html : this.fieldLabel
33819 if(this.indicatorpos == 'left'){
33822 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33823 tooltip : 'This field is required'
33828 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33829 tooltip : 'This field is required'
33835 cls : 'roo-radio-set-items'
33838 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33840 if (align === 'left' && this.fieldLabel.length) {
33843 cls : "roo-radio-set-right",
33849 if(this.labelWidth > 12){
33850 label.style = "width: " + this.labelWidth + 'px';
33853 if(this.labelWidth < 13 && this.labelmd == 0){
33854 this.labelmd = this.labelWidth;
33857 if(this.labellg > 0){
33858 label.cls += ' col-lg-' + this.labellg;
33859 items.cls += ' col-lg-' + (12 - this.labellg);
33862 if(this.labelmd > 0){
33863 label.cls += ' col-md-' + this.labelmd;
33864 items.cls += ' col-md-' + (12 - this.labelmd);
33867 if(this.labelsm > 0){
33868 label.cls += ' col-sm-' + this.labelsm;
33869 items.cls += ' col-sm-' + (12 - this.labelsm);
33872 if(this.labelxs > 0){
33873 label.cls += ' col-xs-' + this.labelxs;
33874 items.cls += ' col-xs-' + (12 - this.labelxs);
33880 cls : 'roo-radio-set',
33884 cls : 'roo-radio-set-input',
33887 value : this.value ? this.value : ''
33894 if(this.weight.length){
33895 cfg.cls += ' roo-radio-' + this.weight;
33899 cfg.cls += ' roo-radio-set-inline';
33903 ['xs','sm','md','lg'].map(function(size){
33904 if (settings[size]) {
33905 cfg.cls += ' col-' + size + '-' + settings[size];
33913 initEvents : function()
33915 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33916 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33918 if(!this.fieldLabel.length){
33919 this.labelEl.hide();
33922 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33923 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33925 this.indicator = this.indicatorEl();
33927 if(this.indicator){
33928 this.indicator.addClass('invisible');
33931 this.originalValue = this.getValue();
33935 inputEl: function ()
33937 return this.el.select('.roo-radio-set-input', true).first();
33940 getChildContainer : function()
33942 return this.itemsEl;
33945 register : function(item)
33947 this.radioes.push(item);
33951 validate : function()
33953 if(this.getVisibilityEl().hasClass('hidden')){
33959 Roo.each(this.radioes, function(i){
33968 if(this.allowBlank) {
33972 if(this.disabled || valid){
33977 this.markInvalid();
33982 markValid : function()
33984 if(this.labelEl.isVisible(true)){
33985 this.indicatorEl().removeClass('visible');
33986 this.indicatorEl().addClass('invisible');
33989 this.el.removeClass([this.invalidClass, this.validClass]);
33990 this.el.addClass(this.validClass);
33992 this.fireEvent('valid', this);
33995 markInvalid : function(msg)
33997 if(this.allowBlank || this.disabled){
34001 if(this.labelEl.isVisible(true)){
34002 this.indicatorEl().removeClass('invisible');
34003 this.indicatorEl().addClass('visible');
34006 this.el.removeClass([this.invalidClass, this.validClass]);
34007 this.el.addClass(this.invalidClass);
34009 this.fireEvent('invalid', this, msg);
34013 setValue : function(v, suppressEvent)
34015 if(this.value === v){
34022 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34025 Roo.each(this.radioes, function(i){
34027 i.el.removeClass('checked');
34030 Roo.each(this.radioes, function(i){
34032 if(i.value === v || i.value.toString() === v.toString()){
34034 i.el.addClass('checked');
34036 if(suppressEvent !== true){
34037 this.fireEvent('check', this, i);
34048 clearInvalid : function(){
34050 if(!this.el || this.preventMark){
34054 this.el.removeClass([this.invalidClass]);
34056 this.fireEvent('valid', this);
34061 Roo.apply(Roo.bootstrap.RadioSet, {
34065 register : function(set)
34067 this.groups[set.name] = set;
34070 get: function(name)
34072 if (typeof(this.groups[name]) == 'undefined') {
34076 return this.groups[name] ;
34082 * Ext JS Library 1.1.1
34083 * Copyright(c) 2006-2007, Ext JS, LLC.
34085 * Originally Released Under LGPL - original licence link has changed is not relivant.
34088 * <script type="text/javascript">
34093 * @class Roo.bootstrap.SplitBar
34094 * @extends Roo.util.Observable
34095 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34099 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34100 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34101 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34102 split.minSize = 100;
34103 split.maxSize = 600;
34104 split.animate = true;
34105 split.on('moved', splitterMoved);
34108 * Create a new SplitBar
34109 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34110 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34111 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34112 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34113 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34114 position of the SplitBar).
34116 Roo.bootstrap.SplitBar = function(cfg){
34121 // dragElement : elm
34122 // resizingElement: el,
34124 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34125 // placement : Roo.bootstrap.SplitBar.LEFT ,
34126 // existingProxy ???
34129 this.el = Roo.get(cfg.dragElement, true);
34130 this.el.dom.unselectable = "on";
34132 this.resizingEl = Roo.get(cfg.resizingElement, true);
34136 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34137 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34140 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34143 * The minimum size of the resizing element. (Defaults to 0)
34149 * The maximum size of the resizing element. (Defaults to 2000)
34152 this.maxSize = 2000;
34155 * Whether to animate the transition to the new size
34158 this.animate = false;
34161 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34164 this.useShim = false;
34169 if(!cfg.existingProxy){
34171 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34173 this.proxy = Roo.get(cfg.existingProxy).dom;
34176 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34179 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34182 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34185 this.dragSpecs = {};
34188 * @private The adapter to use to positon and resize elements
34190 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34191 this.adapter.init(this);
34193 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34195 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34196 this.el.addClass("roo-splitbar-h");
34199 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34200 this.el.addClass("roo-splitbar-v");
34206 * Fires when the splitter is moved (alias for {@link #event-moved})
34207 * @param {Roo.bootstrap.SplitBar} this
34208 * @param {Number} newSize the new width or height
34213 * Fires when the splitter is moved
34214 * @param {Roo.bootstrap.SplitBar} this
34215 * @param {Number} newSize the new width or height
34219 * @event beforeresize
34220 * Fires before the splitter is dragged
34221 * @param {Roo.bootstrap.SplitBar} this
34223 "beforeresize" : true,
34225 "beforeapply" : true
34228 Roo.util.Observable.call(this);
34231 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34232 onStartProxyDrag : function(x, y){
34233 this.fireEvent("beforeresize", this);
34235 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34237 o.enableDisplayMode("block");
34238 // all splitbars share the same overlay
34239 Roo.bootstrap.SplitBar.prototype.overlay = o;
34241 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34242 this.overlay.show();
34243 Roo.get(this.proxy).setDisplayed("block");
34244 var size = this.adapter.getElementSize(this);
34245 this.activeMinSize = this.getMinimumSize();;
34246 this.activeMaxSize = this.getMaximumSize();;
34247 var c1 = size - this.activeMinSize;
34248 var c2 = Math.max(this.activeMaxSize - size, 0);
34249 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34250 this.dd.resetConstraints();
34251 this.dd.setXConstraint(
34252 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34253 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34255 this.dd.setYConstraint(0, 0);
34257 this.dd.resetConstraints();
34258 this.dd.setXConstraint(0, 0);
34259 this.dd.setYConstraint(
34260 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34261 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34264 this.dragSpecs.startSize = size;
34265 this.dragSpecs.startPoint = [x, y];
34266 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34270 * @private Called after the drag operation by the DDProxy
34272 onEndProxyDrag : function(e){
34273 Roo.get(this.proxy).setDisplayed(false);
34274 var endPoint = Roo.lib.Event.getXY(e);
34276 this.overlay.hide();
34279 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34280 newSize = this.dragSpecs.startSize +
34281 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34282 endPoint[0] - this.dragSpecs.startPoint[0] :
34283 this.dragSpecs.startPoint[0] - endPoint[0]
34286 newSize = this.dragSpecs.startSize +
34287 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34288 endPoint[1] - this.dragSpecs.startPoint[1] :
34289 this.dragSpecs.startPoint[1] - endPoint[1]
34292 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34293 if(newSize != this.dragSpecs.startSize){
34294 if(this.fireEvent('beforeapply', this, newSize) !== false){
34295 this.adapter.setElementSize(this, newSize);
34296 this.fireEvent("moved", this, newSize);
34297 this.fireEvent("resize", this, newSize);
34303 * Get the adapter this SplitBar uses
34304 * @return The adapter object
34306 getAdapter : function(){
34307 return this.adapter;
34311 * Set the adapter this SplitBar uses
34312 * @param {Object} adapter A SplitBar adapter object
34314 setAdapter : function(adapter){
34315 this.adapter = adapter;
34316 this.adapter.init(this);
34320 * Gets the minimum size for the resizing element
34321 * @return {Number} The minimum size
34323 getMinimumSize : function(){
34324 return this.minSize;
34328 * Sets the minimum size for the resizing element
34329 * @param {Number} minSize The minimum size
34331 setMinimumSize : function(minSize){
34332 this.minSize = minSize;
34336 * Gets the maximum size for the resizing element
34337 * @return {Number} The maximum size
34339 getMaximumSize : function(){
34340 return this.maxSize;
34344 * Sets the maximum size for the resizing element
34345 * @param {Number} maxSize The maximum size
34347 setMaximumSize : function(maxSize){
34348 this.maxSize = maxSize;
34352 * Sets the initialize size for the resizing element
34353 * @param {Number} size The initial size
34355 setCurrentSize : function(size){
34356 var oldAnimate = this.animate;
34357 this.animate = false;
34358 this.adapter.setElementSize(this, size);
34359 this.animate = oldAnimate;
34363 * Destroy this splitbar.
34364 * @param {Boolean} removeEl True to remove the element
34366 destroy : function(removeEl){
34368 this.shim.remove();
34371 this.proxy.parentNode.removeChild(this.proxy);
34379 * @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.
34381 Roo.bootstrap.SplitBar.createProxy = function(dir){
34382 var proxy = new Roo.Element(document.createElement("div"));
34383 proxy.unselectable();
34384 var cls = 'roo-splitbar-proxy';
34385 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34386 document.body.appendChild(proxy.dom);
34391 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34392 * Default Adapter. It assumes the splitter and resizing element are not positioned
34393 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34395 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34398 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34399 // do nothing for now
34400 init : function(s){
34404 * Called before drag operations to get the current size of the resizing element.
34405 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34407 getElementSize : function(s){
34408 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34409 return s.resizingEl.getWidth();
34411 return s.resizingEl.getHeight();
34416 * Called after drag operations to set the size of the resizing element.
34417 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34418 * @param {Number} newSize The new size to set
34419 * @param {Function} onComplete A function to be invoked when resizing is complete
34421 setElementSize : function(s, newSize, onComplete){
34422 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34424 s.resizingEl.setWidth(newSize);
34426 onComplete(s, newSize);
34429 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34434 s.resizingEl.setHeight(newSize);
34436 onComplete(s, newSize);
34439 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34446 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34447 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34448 * Adapter that moves the splitter element to align with the resized sizing element.
34449 * Used with an absolute positioned SplitBar.
34450 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34451 * document.body, make sure you assign an id to the body element.
34453 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34454 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34455 this.container = Roo.get(container);
34458 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34459 init : function(s){
34460 this.basic.init(s);
34463 getElementSize : function(s){
34464 return this.basic.getElementSize(s);
34467 setElementSize : function(s, newSize, onComplete){
34468 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34471 moveSplitter : function(s){
34472 var yes = Roo.bootstrap.SplitBar;
34473 switch(s.placement){
34475 s.el.setX(s.resizingEl.getRight());
34478 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34481 s.el.setY(s.resizingEl.getBottom());
34484 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34491 * Orientation constant - Create a vertical SplitBar
34495 Roo.bootstrap.SplitBar.VERTICAL = 1;
34498 * Orientation constant - Create a horizontal SplitBar
34502 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34505 * Placement constant - The resizing element is to the left of the splitter element
34509 Roo.bootstrap.SplitBar.LEFT = 1;
34512 * Placement constant - The resizing element is to the right of the splitter element
34516 Roo.bootstrap.SplitBar.RIGHT = 2;
34519 * Placement constant - The resizing element is positioned above the splitter element
34523 Roo.bootstrap.SplitBar.TOP = 3;
34526 * Placement constant - The resizing element is positioned under splitter element
34530 Roo.bootstrap.SplitBar.BOTTOM = 4;
34531 Roo.namespace("Roo.bootstrap.layout");/*
34533 * Ext JS Library 1.1.1
34534 * Copyright(c) 2006-2007, Ext JS, LLC.
34536 * Originally Released Under LGPL - original licence link has changed is not relivant.
34539 * <script type="text/javascript">
34543 * @class Roo.bootstrap.layout.Manager
34544 * @extends Roo.bootstrap.Component
34545 * Base class for layout managers.
34547 Roo.bootstrap.layout.Manager = function(config)
34549 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34555 /** false to disable window resize monitoring @type Boolean */
34556 this.monitorWindowResize = true;
34561 * Fires when a layout is performed.
34562 * @param {Roo.LayoutManager} this
34566 * @event regionresized
34567 * Fires when the user resizes a region.
34568 * @param {Roo.LayoutRegion} region The resized region
34569 * @param {Number} newSize The new size (width for east/west, height for north/south)
34571 "regionresized" : true,
34573 * @event regioncollapsed
34574 * Fires when a region is collapsed.
34575 * @param {Roo.LayoutRegion} region The collapsed region
34577 "regioncollapsed" : true,
34579 * @event regionexpanded
34580 * Fires when a region is expanded.
34581 * @param {Roo.LayoutRegion} region The expanded region
34583 "regionexpanded" : true
34585 this.updating = false;
34588 this.el = Roo.get(config.el);
34594 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34599 monitorWindowResize : true,
34605 onRender : function(ct, position)
34608 this.el = Roo.get(ct);
34611 //this.fireEvent('render',this);
34615 initEvents: function()
34619 // ie scrollbar fix
34620 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34621 document.body.scroll = "no";
34622 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34623 this.el.position('relative');
34625 this.id = this.el.id;
34626 this.el.addClass("roo-layout-container");
34627 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34628 if(this.el.dom != document.body ) {
34629 this.el.on('resize', this.layout,this);
34630 this.el.on('show', this.layout,this);
34636 * Returns true if this layout is currently being updated
34637 * @return {Boolean}
34639 isUpdating : function(){
34640 return this.updating;
34644 * Suspend the LayoutManager from doing auto-layouts while
34645 * making multiple add or remove calls
34647 beginUpdate : function(){
34648 this.updating = true;
34652 * Restore auto-layouts and optionally disable the manager from performing a layout
34653 * @param {Boolean} noLayout true to disable a layout update
34655 endUpdate : function(noLayout){
34656 this.updating = false;
34662 layout: function(){
34666 onRegionResized : function(region, newSize){
34667 this.fireEvent("regionresized", region, newSize);
34671 onRegionCollapsed : function(region){
34672 this.fireEvent("regioncollapsed", region);
34675 onRegionExpanded : function(region){
34676 this.fireEvent("regionexpanded", region);
34680 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34681 * performs box-model adjustments.
34682 * @return {Object} The size as an object {width: (the width), height: (the height)}
34684 getViewSize : function()
34687 if(this.el.dom != document.body){
34688 size = this.el.getSize();
34690 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34692 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34693 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34698 * Returns the Element this layout is bound to.
34699 * @return {Roo.Element}
34701 getEl : function(){
34706 * Returns the specified region.
34707 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34708 * @return {Roo.LayoutRegion}
34710 getRegion : function(target){
34711 return this.regions[target.toLowerCase()];
34714 onWindowResize : function(){
34715 if(this.monitorWindowResize){
34722 * Ext JS Library 1.1.1
34723 * Copyright(c) 2006-2007, Ext JS, LLC.
34725 * Originally Released Under LGPL - original licence link has changed is not relivant.
34728 * <script type="text/javascript">
34731 * @class Roo.bootstrap.layout.Border
34732 * @extends Roo.bootstrap.layout.Manager
34733 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34734 * please see: examples/bootstrap/nested.html<br><br>
34736 <b>The container the layout is rendered into can be either the body element or any other element.
34737 If it is not the body element, the container needs to either be an absolute positioned element,
34738 or you will need to add "position:relative" to the css of the container. You will also need to specify
34739 the container size if it is not the body element.</b>
34742 * Create a new Border
34743 * @param {Object} config Configuration options
34745 Roo.bootstrap.layout.Border = function(config){
34746 config = config || {};
34747 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34751 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34752 if(config[region]){
34753 config[region].region = region;
34754 this.addRegion(config[region]);
34760 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34762 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34764 * Creates and adds a new region if it doesn't already exist.
34765 * @param {String} target The target region key (north, south, east, west or center).
34766 * @param {Object} config The regions config object
34767 * @return {BorderLayoutRegion} The new region
34769 addRegion : function(config)
34771 if(!this.regions[config.region]){
34772 var r = this.factory(config);
34773 this.bindRegion(r);
34775 return this.regions[config.region];
34779 bindRegion : function(r){
34780 this.regions[r.config.region] = r;
34782 r.on("visibilitychange", this.layout, this);
34783 r.on("paneladded", this.layout, this);
34784 r.on("panelremoved", this.layout, this);
34785 r.on("invalidated", this.layout, this);
34786 r.on("resized", this.onRegionResized, this);
34787 r.on("collapsed", this.onRegionCollapsed, this);
34788 r.on("expanded", this.onRegionExpanded, this);
34792 * Performs a layout update.
34794 layout : function()
34796 if(this.updating) {
34800 // render all the rebions if they have not been done alreayd?
34801 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34802 if(this.regions[region] && !this.regions[region].bodyEl){
34803 this.regions[region].onRender(this.el)
34807 var size = this.getViewSize();
34808 var w = size.width;
34809 var h = size.height;
34814 //var x = 0, y = 0;
34816 var rs = this.regions;
34817 var north = rs["north"];
34818 var south = rs["south"];
34819 var west = rs["west"];
34820 var east = rs["east"];
34821 var center = rs["center"];
34822 //if(this.hideOnLayout){ // not supported anymore
34823 //c.el.setStyle("display", "none");
34825 if(north && north.isVisible()){
34826 var b = north.getBox();
34827 var m = north.getMargins();
34828 b.width = w - (m.left+m.right);
34831 centerY = b.height + b.y + m.bottom;
34832 centerH -= centerY;
34833 north.updateBox(this.safeBox(b));
34835 if(south && south.isVisible()){
34836 var b = south.getBox();
34837 var m = south.getMargins();
34838 b.width = w - (m.left+m.right);
34840 var totalHeight = (b.height + m.top + m.bottom);
34841 b.y = h - totalHeight + m.top;
34842 centerH -= totalHeight;
34843 south.updateBox(this.safeBox(b));
34845 if(west && west.isVisible()){
34846 var b = west.getBox();
34847 var m = west.getMargins();
34848 b.height = centerH - (m.top+m.bottom);
34850 b.y = centerY + m.top;
34851 var totalWidth = (b.width + m.left + m.right);
34852 centerX += totalWidth;
34853 centerW -= totalWidth;
34854 west.updateBox(this.safeBox(b));
34856 if(east && east.isVisible()){
34857 var b = east.getBox();
34858 var m = east.getMargins();
34859 b.height = centerH - (m.top+m.bottom);
34860 var totalWidth = (b.width + m.left + m.right);
34861 b.x = w - totalWidth + m.left;
34862 b.y = centerY + m.top;
34863 centerW -= totalWidth;
34864 east.updateBox(this.safeBox(b));
34867 var m = center.getMargins();
34869 x: centerX + m.left,
34870 y: centerY + m.top,
34871 width: centerW - (m.left+m.right),
34872 height: centerH - (m.top+m.bottom)
34874 //if(this.hideOnLayout){
34875 //center.el.setStyle("display", "block");
34877 center.updateBox(this.safeBox(centerBox));
34880 this.fireEvent("layout", this);
34884 safeBox : function(box){
34885 box.width = Math.max(0, box.width);
34886 box.height = Math.max(0, box.height);
34891 * Adds a ContentPanel (or subclass) to this layout.
34892 * @param {String} target The target region key (north, south, east, west or center).
34893 * @param {Roo.ContentPanel} panel The panel to add
34894 * @return {Roo.ContentPanel} The added panel
34896 add : function(target, panel){
34898 target = target.toLowerCase();
34899 return this.regions[target].add(panel);
34903 * Remove a ContentPanel (or subclass) to this layout.
34904 * @param {String} target The target region key (north, south, east, west or center).
34905 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34906 * @return {Roo.ContentPanel} The removed panel
34908 remove : function(target, panel){
34909 target = target.toLowerCase();
34910 return this.regions[target].remove(panel);
34914 * Searches all regions for a panel with the specified id
34915 * @param {String} panelId
34916 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34918 findPanel : function(panelId){
34919 var rs = this.regions;
34920 for(var target in rs){
34921 if(typeof rs[target] != "function"){
34922 var p = rs[target].getPanel(panelId);
34932 * Searches all regions for a panel with the specified id and activates (shows) it.
34933 * @param {String/ContentPanel} panelId The panels id or the panel itself
34934 * @return {Roo.ContentPanel} The shown panel or null
34936 showPanel : function(panelId) {
34937 var rs = this.regions;
34938 for(var target in rs){
34939 var r = rs[target];
34940 if(typeof r != "function"){
34941 if(r.hasPanel(panelId)){
34942 return r.showPanel(panelId);
34950 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34951 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34954 restoreState : function(provider){
34956 provider = Roo.state.Manager;
34958 var sm = new Roo.LayoutStateManager();
34959 sm.init(this, provider);
34965 * Adds a xtype elements to the layout.
34969 xtype : 'ContentPanel',
34976 xtype : 'NestedLayoutPanel',
34982 items : [ ... list of content panels or nested layout panels.. ]
34986 * @param {Object} cfg Xtype definition of item to add.
34988 addxtype : function(cfg)
34990 // basically accepts a pannel...
34991 // can accept a layout region..!?!?
34992 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34995 // theory? children can only be panels??
34997 //if (!cfg.xtype.match(/Panel$/)) {
35002 if (typeof(cfg.region) == 'undefined') {
35003 Roo.log("Failed to add Panel, region was not set");
35007 var region = cfg.region;
35013 xitems = cfg.items;
35020 case 'Content': // ContentPanel (el, cfg)
35021 case 'Scroll': // ContentPanel (el, cfg)
35023 cfg.autoCreate = true;
35024 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35026 // var el = this.el.createChild();
35027 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35030 this.add(region, ret);
35034 case 'TreePanel': // our new panel!
35035 cfg.el = this.el.createChild();
35036 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35037 this.add(region, ret);
35042 // create a new Layout (which is a Border Layout...
35044 var clayout = cfg.layout;
35045 clayout.el = this.el.createChild();
35046 clayout.items = clayout.items || [];
35050 // replace this exitems with the clayout ones..
35051 xitems = clayout.items;
35053 // force background off if it's in center...
35054 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35055 cfg.background = false;
35057 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35060 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35061 //console.log('adding nested layout panel ' + cfg.toSource());
35062 this.add(region, ret);
35063 nb = {}; /// find first...
35068 // needs grid and region
35070 //var el = this.getRegion(region).el.createChild();
35072 *var el = this.el.createChild();
35073 // create the grid first...
35074 cfg.grid.container = el;
35075 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35078 if (region == 'center' && this.active ) {
35079 cfg.background = false;
35082 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35084 this.add(region, ret);
35086 if (cfg.background) {
35087 // render grid on panel activation (if panel background)
35088 ret.on('activate', function(gp) {
35089 if (!gp.grid.rendered) {
35090 // gp.grid.render(el);
35094 // cfg.grid.render(el);
35100 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35101 // it was the old xcomponent building that caused this before.
35102 // espeically if border is the top element in the tree.
35112 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35114 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35115 this.add(region, ret);
35119 throw "Can not add '" + cfg.xtype + "' to Border";
35125 this.beginUpdate();
35129 Roo.each(xitems, function(i) {
35130 region = nb && i.region ? i.region : false;
35132 var add = ret.addxtype(i);
35135 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35136 if (!i.background) {
35137 abn[region] = nb[region] ;
35144 // make the last non-background panel active..
35145 //if (nb) { Roo.log(abn); }
35148 for(var r in abn) {
35149 region = this.getRegion(r);
35151 // tried using nb[r], but it does not work..
35153 region.showPanel(abn[r]);
35164 factory : function(cfg)
35167 var validRegions = Roo.bootstrap.layout.Border.regions;
35169 var target = cfg.region;
35172 var r = Roo.bootstrap.layout;
35176 return new r.North(cfg);
35178 return new r.South(cfg);
35180 return new r.East(cfg);
35182 return new r.West(cfg);
35184 return new r.Center(cfg);
35186 throw 'Layout region "'+target+'" not supported.';
35193 * Ext JS Library 1.1.1
35194 * Copyright(c) 2006-2007, Ext JS, LLC.
35196 * Originally Released Under LGPL - original licence link has changed is not relivant.
35199 * <script type="text/javascript">
35203 * @class Roo.bootstrap.layout.Basic
35204 * @extends Roo.util.Observable
35205 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35206 * and does not have a titlebar, tabs or any other features. All it does is size and position
35207 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35208 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35209 * @cfg {string} region the region that it inhabits..
35210 * @cfg {bool} skipConfig skip config?
35214 Roo.bootstrap.layout.Basic = function(config){
35216 this.mgr = config.mgr;
35218 this.position = config.region;
35220 var skipConfig = config.skipConfig;
35224 * @scope Roo.BasicLayoutRegion
35228 * @event beforeremove
35229 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35230 * @param {Roo.LayoutRegion} this
35231 * @param {Roo.ContentPanel} panel The panel
35232 * @param {Object} e The cancel event object
35234 "beforeremove" : true,
35236 * @event invalidated
35237 * Fires when the layout for this region is changed.
35238 * @param {Roo.LayoutRegion} this
35240 "invalidated" : true,
35242 * @event visibilitychange
35243 * Fires when this region is shown or hidden
35244 * @param {Roo.LayoutRegion} this
35245 * @param {Boolean} visibility true or false
35247 "visibilitychange" : true,
35249 * @event paneladded
35250 * Fires when a panel is added.
35251 * @param {Roo.LayoutRegion} this
35252 * @param {Roo.ContentPanel} panel The panel
35254 "paneladded" : true,
35256 * @event panelremoved
35257 * Fires when a panel is removed.
35258 * @param {Roo.LayoutRegion} this
35259 * @param {Roo.ContentPanel} panel The panel
35261 "panelremoved" : true,
35263 * @event beforecollapse
35264 * Fires when this region before collapse.
35265 * @param {Roo.LayoutRegion} this
35267 "beforecollapse" : true,
35270 * Fires when this region is collapsed.
35271 * @param {Roo.LayoutRegion} this
35273 "collapsed" : true,
35276 * Fires when this region is expanded.
35277 * @param {Roo.LayoutRegion} this
35282 * Fires when this region is slid into view.
35283 * @param {Roo.LayoutRegion} this
35285 "slideshow" : true,
35288 * Fires when this region slides out of view.
35289 * @param {Roo.LayoutRegion} this
35291 "slidehide" : true,
35293 * @event panelactivated
35294 * Fires when a panel is activated.
35295 * @param {Roo.LayoutRegion} this
35296 * @param {Roo.ContentPanel} panel The activated panel
35298 "panelactivated" : true,
35301 * Fires when the user resizes this region.
35302 * @param {Roo.LayoutRegion} this
35303 * @param {Number} newSize The new size (width for east/west, height for north/south)
35307 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35308 this.panels = new Roo.util.MixedCollection();
35309 this.panels.getKey = this.getPanelId.createDelegate(this);
35311 this.activePanel = null;
35312 // ensure listeners are added...
35314 if (config.listeners || config.events) {
35315 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35316 listeners : config.listeners || {},
35317 events : config.events || {}
35321 if(skipConfig !== true){
35322 this.applyConfig(config);
35326 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35328 getPanelId : function(p){
35332 applyConfig : function(config){
35333 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35334 this.config = config;
35339 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35340 * the width, for horizontal (north, south) the height.
35341 * @param {Number} newSize The new width or height
35343 resizeTo : function(newSize){
35344 var el = this.el ? this.el :
35345 (this.activePanel ? this.activePanel.getEl() : null);
35347 switch(this.position){
35350 el.setWidth(newSize);
35351 this.fireEvent("resized", this, newSize);
35355 el.setHeight(newSize);
35356 this.fireEvent("resized", this, newSize);
35362 getBox : function(){
35363 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35366 getMargins : function(){
35367 return this.margins;
35370 updateBox : function(box){
35372 var el = this.activePanel.getEl();
35373 el.dom.style.left = box.x + "px";
35374 el.dom.style.top = box.y + "px";
35375 this.activePanel.setSize(box.width, box.height);
35379 * Returns the container element for this region.
35380 * @return {Roo.Element}
35382 getEl : function(){
35383 return this.activePanel;
35387 * Returns true if this region is currently visible.
35388 * @return {Boolean}
35390 isVisible : function(){
35391 return this.activePanel ? true : false;
35394 setActivePanel : function(panel){
35395 panel = this.getPanel(panel);
35396 if(this.activePanel && this.activePanel != panel){
35397 this.activePanel.setActiveState(false);
35398 this.activePanel.getEl().setLeftTop(-10000,-10000);
35400 this.activePanel = panel;
35401 panel.setActiveState(true);
35403 panel.setSize(this.box.width, this.box.height);
35405 this.fireEvent("panelactivated", this, panel);
35406 this.fireEvent("invalidated");
35410 * Show the specified panel.
35411 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35412 * @return {Roo.ContentPanel} The shown panel or null
35414 showPanel : function(panel){
35415 panel = this.getPanel(panel);
35417 this.setActivePanel(panel);
35423 * Get the active panel for this region.
35424 * @return {Roo.ContentPanel} The active panel or null
35426 getActivePanel : function(){
35427 return this.activePanel;
35431 * Add the passed ContentPanel(s)
35432 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35433 * @return {Roo.ContentPanel} The panel added (if only one was added)
35435 add : function(panel){
35436 if(arguments.length > 1){
35437 for(var i = 0, len = arguments.length; i < len; i++) {
35438 this.add(arguments[i]);
35442 if(this.hasPanel(panel)){
35443 this.showPanel(panel);
35446 var el = panel.getEl();
35447 if(el.dom.parentNode != this.mgr.el.dom){
35448 this.mgr.el.dom.appendChild(el.dom);
35450 if(panel.setRegion){
35451 panel.setRegion(this);
35453 this.panels.add(panel);
35454 el.setStyle("position", "absolute");
35455 if(!panel.background){
35456 this.setActivePanel(panel);
35457 if(this.config.initialSize && this.panels.getCount()==1){
35458 this.resizeTo(this.config.initialSize);
35461 this.fireEvent("paneladded", this, panel);
35466 * Returns true if the panel is in this region.
35467 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35468 * @return {Boolean}
35470 hasPanel : function(panel){
35471 if(typeof panel == "object"){ // must be panel obj
35472 panel = panel.getId();
35474 return this.getPanel(panel) ? true : false;
35478 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35479 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35480 * @param {Boolean} preservePanel Overrides the config preservePanel option
35481 * @return {Roo.ContentPanel} The panel that was removed
35483 remove : function(panel, preservePanel){
35484 panel = this.getPanel(panel);
35489 this.fireEvent("beforeremove", this, panel, e);
35490 if(e.cancel === true){
35493 var panelId = panel.getId();
35494 this.panels.removeKey(panelId);
35499 * Returns the panel specified or null if it's not in this region.
35500 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35501 * @return {Roo.ContentPanel}
35503 getPanel : function(id){
35504 if(typeof id == "object"){ // must be panel obj
35507 return this.panels.get(id);
35511 * Returns this regions position (north/south/east/west/center).
35514 getPosition: function(){
35515 return this.position;
35519 * Ext JS Library 1.1.1
35520 * Copyright(c) 2006-2007, Ext JS, LLC.
35522 * Originally Released Under LGPL - original licence link has changed is not relivant.
35525 * <script type="text/javascript">
35529 * @class Roo.bootstrap.layout.Region
35530 * @extends Roo.bootstrap.layout.Basic
35531 * This class represents a region in a layout manager.
35533 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35534 * @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})
35535 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35536 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35537 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35538 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35539 * @cfg {String} title The title for the region (overrides panel titles)
35540 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35541 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35542 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35543 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35544 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35545 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35546 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35547 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35548 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35549 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35551 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35552 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35553 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35554 * @cfg {Number} width For East/West panels
35555 * @cfg {Number} height For North/South panels
35556 * @cfg {Boolean} split To show the splitter
35557 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35559 * @cfg {string} cls Extra CSS classes to add to region
35561 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35562 * @cfg {string} region the region that it inhabits..
35565 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35566 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35568 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35569 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35570 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35572 Roo.bootstrap.layout.Region = function(config)
35574 this.applyConfig(config);
35576 var mgr = config.mgr;
35577 var pos = config.region;
35578 config.skipConfig = true;
35579 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35582 this.onRender(mgr.el);
35585 this.visible = true;
35586 this.collapsed = false;
35587 this.unrendered_panels = [];
35590 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35592 position: '', // set by wrapper (eg. north/south etc..)
35593 unrendered_panels : null, // unrendered panels.
35594 createBody : function(){
35595 /** This region's body element
35596 * @type Roo.Element */
35597 this.bodyEl = this.el.createChild({
35599 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35603 onRender: function(ctr, pos)
35605 var dh = Roo.DomHelper;
35606 /** This region's container element
35607 * @type Roo.Element */
35608 this.el = dh.append(ctr.dom, {
35610 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35612 /** This region's title element
35613 * @type Roo.Element */
35615 this.titleEl = dh.append(this.el.dom,
35618 unselectable: "on",
35619 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35621 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35622 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35625 this.titleEl.enableDisplayMode();
35626 /** This region's title text element
35627 * @type HTMLElement */
35628 this.titleTextEl = this.titleEl.dom.firstChild;
35629 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35631 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35632 this.closeBtn.enableDisplayMode();
35633 this.closeBtn.on("click", this.closeClicked, this);
35634 this.closeBtn.hide();
35636 this.createBody(this.config);
35637 if(this.config.hideWhenEmpty){
35639 this.on("paneladded", this.validateVisibility, this);
35640 this.on("panelremoved", this.validateVisibility, this);
35642 if(this.autoScroll){
35643 this.bodyEl.setStyle("overflow", "auto");
35645 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35647 //if(c.titlebar !== false){
35648 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35649 this.titleEl.hide();
35651 this.titleEl.show();
35652 if(this.config.title){
35653 this.titleTextEl.innerHTML = this.config.title;
35657 if(this.config.collapsed){
35658 this.collapse(true);
35660 if(this.config.hidden){
35664 if (this.unrendered_panels && this.unrendered_panels.length) {
35665 for (var i =0;i< this.unrendered_panels.length; i++) {
35666 this.add(this.unrendered_panels[i]);
35668 this.unrendered_panels = null;
35674 applyConfig : function(c)
35677 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35678 var dh = Roo.DomHelper;
35679 if(c.titlebar !== false){
35680 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35681 this.collapseBtn.on("click", this.collapse, this);
35682 this.collapseBtn.enableDisplayMode();
35684 if(c.showPin === true || this.showPin){
35685 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35686 this.stickBtn.enableDisplayMode();
35687 this.stickBtn.on("click", this.expand, this);
35688 this.stickBtn.hide();
35693 /** This region's collapsed element
35694 * @type Roo.Element */
35697 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35698 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35701 if(c.floatable !== false){
35702 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35703 this.collapsedEl.on("click", this.collapseClick, this);
35706 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35707 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35708 id: "message", unselectable: "on", style:{"float":"left"}});
35709 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35711 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35712 this.expandBtn.on("click", this.expand, this);
35716 if(this.collapseBtn){
35717 this.collapseBtn.setVisible(c.collapsible == true);
35720 this.cmargins = c.cmargins || this.cmargins ||
35721 (this.position == "west" || this.position == "east" ?
35722 {top: 0, left: 2, right:2, bottom: 0} :
35723 {top: 2, left: 0, right:0, bottom: 2});
35725 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35728 this.bottomTabs = c.tabPosition != "top";
35730 this.autoScroll = c.autoScroll || false;
35735 this.duration = c.duration || .30;
35736 this.slideDuration = c.slideDuration || .45;
35741 * Returns true if this region is currently visible.
35742 * @return {Boolean}
35744 isVisible : function(){
35745 return this.visible;
35749 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35750 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35752 //setCollapsedTitle : function(title){
35753 // title = title || " ";
35754 // if(this.collapsedTitleTextEl){
35755 // this.collapsedTitleTextEl.innerHTML = title;
35759 getBox : function(){
35761 // if(!this.collapsed){
35762 b = this.el.getBox(false, true);
35764 // b = this.collapsedEl.getBox(false, true);
35769 getMargins : function(){
35770 return this.margins;
35771 //return this.collapsed ? this.cmargins : this.margins;
35774 highlight : function(){
35775 this.el.addClass("x-layout-panel-dragover");
35778 unhighlight : function(){
35779 this.el.removeClass("x-layout-panel-dragover");
35782 updateBox : function(box)
35784 if (!this.bodyEl) {
35785 return; // not rendered yet..
35789 if(!this.collapsed){
35790 this.el.dom.style.left = box.x + "px";
35791 this.el.dom.style.top = box.y + "px";
35792 this.updateBody(box.width, box.height);
35794 this.collapsedEl.dom.style.left = box.x + "px";
35795 this.collapsedEl.dom.style.top = box.y + "px";
35796 this.collapsedEl.setSize(box.width, box.height);
35799 this.tabs.autoSizeTabs();
35803 updateBody : function(w, h)
35806 this.el.setWidth(w);
35807 w -= this.el.getBorderWidth("rl");
35808 if(this.config.adjustments){
35809 w += this.config.adjustments[0];
35812 if(h !== null && h > 0){
35813 this.el.setHeight(h);
35814 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35815 h -= this.el.getBorderWidth("tb");
35816 if(this.config.adjustments){
35817 h += this.config.adjustments[1];
35819 this.bodyEl.setHeight(h);
35821 h = this.tabs.syncHeight(h);
35824 if(this.panelSize){
35825 w = w !== null ? w : this.panelSize.width;
35826 h = h !== null ? h : this.panelSize.height;
35828 if(this.activePanel){
35829 var el = this.activePanel.getEl();
35830 w = w !== null ? w : el.getWidth();
35831 h = h !== null ? h : el.getHeight();
35832 this.panelSize = {width: w, height: h};
35833 this.activePanel.setSize(w, h);
35835 if(Roo.isIE && this.tabs){
35836 this.tabs.el.repaint();
35841 * Returns the container element for this region.
35842 * @return {Roo.Element}
35844 getEl : function(){
35849 * Hides this region.
35852 //if(!this.collapsed){
35853 this.el.dom.style.left = "-2000px";
35856 // this.collapsedEl.dom.style.left = "-2000px";
35857 // this.collapsedEl.hide();
35859 this.visible = false;
35860 this.fireEvent("visibilitychange", this, false);
35864 * Shows this region if it was previously hidden.
35867 //if(!this.collapsed){
35870 // this.collapsedEl.show();
35872 this.visible = true;
35873 this.fireEvent("visibilitychange", this, true);
35876 closeClicked : function(){
35877 if(this.activePanel){
35878 this.remove(this.activePanel);
35882 collapseClick : function(e){
35884 e.stopPropagation();
35887 e.stopPropagation();
35893 * Collapses this region.
35894 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35897 collapse : function(skipAnim, skipCheck = false){
35898 if(this.collapsed) {
35902 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35904 this.collapsed = true;
35906 this.split.el.hide();
35908 if(this.config.animate && skipAnim !== true){
35909 this.fireEvent("invalidated", this);
35910 this.animateCollapse();
35912 this.el.setLocation(-20000,-20000);
35914 this.collapsedEl.show();
35915 this.fireEvent("collapsed", this);
35916 this.fireEvent("invalidated", this);
35922 animateCollapse : function(){
35927 * Expands this region if it was previously collapsed.
35928 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35929 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35932 expand : function(e, skipAnim){
35934 e.stopPropagation();
35936 if(!this.collapsed || this.el.hasActiveFx()) {
35940 this.afterSlideIn();
35943 this.collapsed = false;
35944 if(this.config.animate && skipAnim !== true){
35945 this.animateExpand();
35949 this.split.el.show();
35951 this.collapsedEl.setLocation(-2000,-2000);
35952 this.collapsedEl.hide();
35953 this.fireEvent("invalidated", this);
35954 this.fireEvent("expanded", this);
35958 animateExpand : function(){
35962 initTabs : function()
35964 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35966 var ts = new Roo.bootstrap.panel.Tabs({
35967 el: this.bodyEl.dom,
35968 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35969 disableTooltips: this.config.disableTabTips,
35970 toolbar : this.config.toolbar
35973 if(this.config.hideTabs){
35974 ts.stripWrap.setDisplayed(false);
35977 ts.resizeTabs = this.config.resizeTabs === true;
35978 ts.minTabWidth = this.config.minTabWidth || 40;
35979 ts.maxTabWidth = this.config.maxTabWidth || 250;
35980 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35981 ts.monitorResize = false;
35982 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35983 ts.bodyEl.addClass('roo-layout-tabs-body');
35984 this.panels.each(this.initPanelAsTab, this);
35987 initPanelAsTab : function(panel){
35988 var ti = this.tabs.addTab(
35992 this.config.closeOnTab && panel.isClosable(),
35995 if(panel.tabTip !== undefined){
35996 ti.setTooltip(panel.tabTip);
35998 ti.on("activate", function(){
35999 this.setActivePanel(panel);
36002 if(this.config.closeOnTab){
36003 ti.on("beforeclose", function(t, e){
36005 this.remove(panel);
36009 panel.tabItem = ti;
36014 updatePanelTitle : function(panel, title)
36016 if(this.activePanel == panel){
36017 this.updateTitle(title);
36020 var ti = this.tabs.getTab(panel.getEl().id);
36022 if(panel.tabTip !== undefined){
36023 ti.setTooltip(panel.tabTip);
36028 updateTitle : function(title){
36029 if(this.titleTextEl && !this.config.title){
36030 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36034 setActivePanel : function(panel)
36036 panel = this.getPanel(panel);
36037 if(this.activePanel && this.activePanel != panel){
36038 if(this.activePanel.setActiveState(false) === false){
36042 this.activePanel = panel;
36043 panel.setActiveState(true);
36044 if(this.panelSize){
36045 panel.setSize(this.panelSize.width, this.panelSize.height);
36048 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36050 this.updateTitle(panel.getTitle());
36052 this.fireEvent("invalidated", this);
36054 this.fireEvent("panelactivated", this, panel);
36058 * Shows the specified panel.
36059 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36060 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36062 showPanel : function(panel)
36064 panel = this.getPanel(panel);
36067 var tab = this.tabs.getTab(panel.getEl().id);
36068 if(tab.isHidden()){
36069 this.tabs.unhideTab(tab.id);
36073 this.setActivePanel(panel);
36080 * Get the active panel for this region.
36081 * @return {Roo.ContentPanel} The active panel or null
36083 getActivePanel : function(){
36084 return this.activePanel;
36087 validateVisibility : function(){
36088 if(this.panels.getCount() < 1){
36089 this.updateTitle(" ");
36090 this.closeBtn.hide();
36093 if(!this.isVisible()){
36100 * Adds the passed ContentPanel(s) to this region.
36101 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36102 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36104 add : function(panel)
36106 if(arguments.length > 1){
36107 for(var i = 0, len = arguments.length; i < len; i++) {
36108 this.add(arguments[i]);
36113 // if we have not been rendered yet, then we can not really do much of this..
36114 if (!this.bodyEl) {
36115 this.unrendered_panels.push(panel);
36122 if(this.hasPanel(panel)){
36123 this.showPanel(panel);
36126 panel.setRegion(this);
36127 this.panels.add(panel);
36128 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36129 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36130 // and hide them... ???
36131 this.bodyEl.dom.appendChild(panel.getEl().dom);
36132 if(panel.background !== true){
36133 this.setActivePanel(panel);
36135 this.fireEvent("paneladded", this, panel);
36142 this.initPanelAsTab(panel);
36146 if(panel.background !== true){
36147 this.tabs.activate(panel.getEl().id);
36149 this.fireEvent("paneladded", this, panel);
36154 * Hides the tab for the specified panel.
36155 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36157 hidePanel : function(panel){
36158 if(this.tabs && (panel = this.getPanel(panel))){
36159 this.tabs.hideTab(panel.getEl().id);
36164 * Unhides the tab for a previously hidden panel.
36165 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36167 unhidePanel : function(panel){
36168 if(this.tabs && (panel = this.getPanel(panel))){
36169 this.tabs.unhideTab(panel.getEl().id);
36173 clearPanels : function(){
36174 while(this.panels.getCount() > 0){
36175 this.remove(this.panels.first());
36180 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36181 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36182 * @param {Boolean} preservePanel Overrides the config preservePanel option
36183 * @return {Roo.ContentPanel} The panel that was removed
36185 remove : function(panel, preservePanel)
36187 panel = this.getPanel(panel);
36192 this.fireEvent("beforeremove", this, panel, e);
36193 if(e.cancel === true){
36196 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36197 var panelId = panel.getId();
36198 this.panels.removeKey(panelId);
36200 document.body.appendChild(panel.getEl().dom);
36203 this.tabs.removeTab(panel.getEl().id);
36204 }else if (!preservePanel){
36205 this.bodyEl.dom.removeChild(panel.getEl().dom);
36207 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36208 var p = this.panels.first();
36209 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36210 tempEl.appendChild(p.getEl().dom);
36211 this.bodyEl.update("");
36212 this.bodyEl.dom.appendChild(p.getEl().dom);
36214 this.updateTitle(p.getTitle());
36216 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36217 this.setActivePanel(p);
36219 panel.setRegion(null);
36220 if(this.activePanel == panel){
36221 this.activePanel = null;
36223 if(this.config.autoDestroy !== false && preservePanel !== true){
36224 try{panel.destroy();}catch(e){}
36226 this.fireEvent("panelremoved", this, panel);
36231 * Returns the TabPanel component used by this region
36232 * @return {Roo.TabPanel}
36234 getTabs : function(){
36238 createTool : function(parentEl, className){
36239 var btn = Roo.DomHelper.append(parentEl, {
36241 cls: "x-layout-tools-button",
36244 cls: "roo-layout-tools-button-inner " + className,
36248 btn.addClassOnOver("roo-layout-tools-button-over");
36253 * Ext JS Library 1.1.1
36254 * Copyright(c) 2006-2007, Ext JS, LLC.
36256 * Originally Released Under LGPL - original licence link has changed is not relivant.
36259 * <script type="text/javascript">
36265 * @class Roo.SplitLayoutRegion
36266 * @extends Roo.LayoutRegion
36267 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36269 Roo.bootstrap.layout.Split = function(config){
36270 this.cursor = config.cursor;
36271 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36274 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36276 splitTip : "Drag to resize.",
36277 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36278 useSplitTips : false,
36280 applyConfig : function(config){
36281 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36284 onRender : function(ctr,pos) {
36286 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36287 if(!this.config.split){
36292 var splitEl = Roo.DomHelper.append(ctr.dom, {
36294 id: this.el.id + "-split",
36295 cls: "roo-layout-split roo-layout-split-"+this.position,
36298 /** The SplitBar for this region
36299 * @type Roo.SplitBar */
36300 // does not exist yet...
36301 Roo.log([this.position, this.orientation]);
36303 this.split = new Roo.bootstrap.SplitBar({
36304 dragElement : splitEl,
36305 resizingElement: this.el,
36306 orientation : this.orientation
36309 this.split.on("moved", this.onSplitMove, this);
36310 this.split.useShim = this.config.useShim === true;
36311 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36312 if(this.useSplitTips){
36313 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36315 //if(config.collapsible){
36316 // this.split.el.on("dblclick", this.collapse, this);
36319 if(typeof this.config.minSize != "undefined"){
36320 this.split.minSize = this.config.minSize;
36322 if(typeof this.config.maxSize != "undefined"){
36323 this.split.maxSize = this.config.maxSize;
36325 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36326 this.hideSplitter();
36331 getHMaxSize : function(){
36332 var cmax = this.config.maxSize || 10000;
36333 var center = this.mgr.getRegion("center");
36334 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36337 getVMaxSize : function(){
36338 var cmax = this.config.maxSize || 10000;
36339 var center = this.mgr.getRegion("center");
36340 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36343 onSplitMove : function(split, newSize){
36344 this.fireEvent("resized", this, newSize);
36348 * Returns the {@link Roo.SplitBar} for this region.
36349 * @return {Roo.SplitBar}
36351 getSplitBar : function(){
36356 this.hideSplitter();
36357 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36360 hideSplitter : function(){
36362 this.split.el.setLocation(-2000,-2000);
36363 this.split.el.hide();
36369 this.split.el.show();
36371 Roo.bootstrap.layout.Split.superclass.show.call(this);
36374 beforeSlide: function(){
36375 if(Roo.isGecko){// firefox overflow auto bug workaround
36376 this.bodyEl.clip();
36378 this.tabs.bodyEl.clip();
36380 if(this.activePanel){
36381 this.activePanel.getEl().clip();
36383 if(this.activePanel.beforeSlide){
36384 this.activePanel.beforeSlide();
36390 afterSlide : function(){
36391 if(Roo.isGecko){// firefox overflow auto bug workaround
36392 this.bodyEl.unclip();
36394 this.tabs.bodyEl.unclip();
36396 if(this.activePanel){
36397 this.activePanel.getEl().unclip();
36398 if(this.activePanel.afterSlide){
36399 this.activePanel.afterSlide();
36405 initAutoHide : function(){
36406 if(this.autoHide !== false){
36407 if(!this.autoHideHd){
36408 var st = new Roo.util.DelayedTask(this.slideIn, this);
36409 this.autoHideHd = {
36410 "mouseout": function(e){
36411 if(!e.within(this.el, true)){
36415 "mouseover" : function(e){
36421 this.el.on(this.autoHideHd);
36425 clearAutoHide : function(){
36426 if(this.autoHide !== false){
36427 this.el.un("mouseout", this.autoHideHd.mouseout);
36428 this.el.un("mouseover", this.autoHideHd.mouseover);
36432 clearMonitor : function(){
36433 Roo.get(document).un("click", this.slideInIf, this);
36436 // these names are backwards but not changed for compat
36437 slideOut : function(){
36438 if(this.isSlid || this.el.hasActiveFx()){
36441 this.isSlid = true;
36442 if(this.collapseBtn){
36443 this.collapseBtn.hide();
36445 this.closeBtnState = this.closeBtn.getStyle('display');
36446 this.closeBtn.hide();
36448 this.stickBtn.show();
36451 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36452 this.beforeSlide();
36453 this.el.setStyle("z-index", 10001);
36454 this.el.slideIn(this.getSlideAnchor(), {
36455 callback: function(){
36457 this.initAutoHide();
36458 Roo.get(document).on("click", this.slideInIf, this);
36459 this.fireEvent("slideshow", this);
36466 afterSlideIn : function(){
36467 this.clearAutoHide();
36468 this.isSlid = false;
36469 this.clearMonitor();
36470 this.el.setStyle("z-index", "");
36471 if(this.collapseBtn){
36472 this.collapseBtn.show();
36474 this.closeBtn.setStyle('display', this.closeBtnState);
36476 this.stickBtn.hide();
36478 this.fireEvent("slidehide", this);
36481 slideIn : function(cb){
36482 if(!this.isSlid || this.el.hasActiveFx()){
36486 this.isSlid = false;
36487 this.beforeSlide();
36488 this.el.slideOut(this.getSlideAnchor(), {
36489 callback: function(){
36490 this.el.setLeftTop(-10000, -10000);
36492 this.afterSlideIn();
36500 slideInIf : function(e){
36501 if(!e.within(this.el)){
36506 animateCollapse : function(){
36507 this.beforeSlide();
36508 this.el.setStyle("z-index", 20000);
36509 var anchor = this.getSlideAnchor();
36510 this.el.slideOut(anchor, {
36511 callback : function(){
36512 this.el.setStyle("z-index", "");
36513 this.collapsedEl.slideIn(anchor, {duration:.3});
36515 this.el.setLocation(-10000,-10000);
36517 this.fireEvent("collapsed", this);
36524 animateExpand : function(){
36525 this.beforeSlide();
36526 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36527 this.el.setStyle("z-index", 20000);
36528 this.collapsedEl.hide({
36531 this.el.slideIn(this.getSlideAnchor(), {
36532 callback : function(){
36533 this.el.setStyle("z-index", "");
36536 this.split.el.show();
36538 this.fireEvent("invalidated", this);
36539 this.fireEvent("expanded", this);
36567 getAnchor : function(){
36568 return this.anchors[this.position];
36571 getCollapseAnchor : function(){
36572 return this.canchors[this.position];
36575 getSlideAnchor : function(){
36576 return this.sanchors[this.position];
36579 getAlignAdj : function(){
36580 var cm = this.cmargins;
36581 switch(this.position){
36597 getExpandAdj : function(){
36598 var c = this.collapsedEl, cm = this.cmargins;
36599 switch(this.position){
36601 return [-(cm.right+c.getWidth()+cm.left), 0];
36604 return [cm.right+c.getWidth()+cm.left, 0];
36607 return [0, -(cm.top+cm.bottom+c.getHeight())];
36610 return [0, cm.top+cm.bottom+c.getHeight()];
36616 * Ext JS Library 1.1.1
36617 * Copyright(c) 2006-2007, Ext JS, LLC.
36619 * Originally Released Under LGPL - original licence link has changed is not relivant.
36622 * <script type="text/javascript">
36625 * These classes are private internal classes
36627 Roo.bootstrap.layout.Center = function(config){
36628 config.region = "center";
36629 Roo.bootstrap.layout.Region.call(this, config);
36630 this.visible = true;
36631 this.minWidth = config.minWidth || 20;
36632 this.minHeight = config.minHeight || 20;
36635 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36637 // center panel can't be hidden
36641 // center panel can't be hidden
36644 getMinWidth: function(){
36645 return this.minWidth;
36648 getMinHeight: function(){
36649 return this.minHeight;
36662 Roo.bootstrap.layout.North = function(config)
36664 config.region = 'north';
36665 config.cursor = 'n-resize';
36667 Roo.bootstrap.layout.Split.call(this, config);
36671 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36672 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36673 this.split.el.addClass("roo-layout-split-v");
36675 var size = config.initialSize || config.height;
36676 if(typeof size != "undefined"){
36677 this.el.setHeight(size);
36680 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36682 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36686 getBox : function(){
36687 if(this.collapsed){
36688 return this.collapsedEl.getBox();
36690 var box = this.el.getBox();
36692 box.height += this.split.el.getHeight();
36697 updateBox : function(box){
36698 if(this.split && !this.collapsed){
36699 box.height -= this.split.el.getHeight();
36700 this.split.el.setLeft(box.x);
36701 this.split.el.setTop(box.y+box.height);
36702 this.split.el.setWidth(box.width);
36704 if(this.collapsed){
36705 this.updateBody(box.width, null);
36707 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36715 Roo.bootstrap.layout.South = function(config){
36716 config.region = 'south';
36717 config.cursor = 's-resize';
36718 Roo.bootstrap.layout.Split.call(this, config);
36720 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36721 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36722 this.split.el.addClass("roo-layout-split-v");
36724 var size = config.initialSize || config.height;
36725 if(typeof size != "undefined"){
36726 this.el.setHeight(size);
36730 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36731 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36732 getBox : function(){
36733 if(this.collapsed){
36734 return this.collapsedEl.getBox();
36736 var box = this.el.getBox();
36738 var sh = this.split.el.getHeight();
36745 updateBox : function(box){
36746 if(this.split && !this.collapsed){
36747 var sh = this.split.el.getHeight();
36750 this.split.el.setLeft(box.x);
36751 this.split.el.setTop(box.y-sh);
36752 this.split.el.setWidth(box.width);
36754 if(this.collapsed){
36755 this.updateBody(box.width, null);
36757 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36761 Roo.bootstrap.layout.East = function(config){
36762 config.region = "east";
36763 config.cursor = "e-resize";
36764 Roo.bootstrap.layout.Split.call(this, config);
36766 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36767 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36768 this.split.el.addClass("roo-layout-split-h");
36770 var size = config.initialSize || config.width;
36771 if(typeof size != "undefined"){
36772 this.el.setWidth(size);
36775 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36776 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36777 getBox : function(){
36778 if(this.collapsed){
36779 return this.collapsedEl.getBox();
36781 var box = this.el.getBox();
36783 var sw = this.split.el.getWidth();
36790 updateBox : function(box){
36791 if(this.split && !this.collapsed){
36792 var sw = this.split.el.getWidth();
36794 this.split.el.setLeft(box.x);
36795 this.split.el.setTop(box.y);
36796 this.split.el.setHeight(box.height);
36799 if(this.collapsed){
36800 this.updateBody(null, box.height);
36802 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36806 Roo.bootstrap.layout.West = function(config){
36807 config.region = "west";
36808 config.cursor = "w-resize";
36810 Roo.bootstrap.layout.Split.call(this, config);
36812 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36813 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36814 this.split.el.addClass("roo-layout-split-h");
36818 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36819 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36821 onRender: function(ctr, pos)
36823 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36824 var size = this.config.initialSize || this.config.width;
36825 if(typeof size != "undefined"){
36826 this.el.setWidth(size);
36830 getBox : function(){
36831 if(this.collapsed){
36832 return this.collapsedEl.getBox();
36834 var box = this.el.getBox();
36836 box.width += this.split.el.getWidth();
36841 updateBox : function(box){
36842 if(this.split && !this.collapsed){
36843 var sw = this.split.el.getWidth();
36845 this.split.el.setLeft(box.x+box.width);
36846 this.split.el.setTop(box.y);
36847 this.split.el.setHeight(box.height);
36849 if(this.collapsed){
36850 this.updateBody(null, box.height);
36852 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36855 Roo.namespace("Roo.bootstrap.panel");/*
36857 * Ext JS Library 1.1.1
36858 * Copyright(c) 2006-2007, Ext JS, LLC.
36860 * Originally Released Under LGPL - original licence link has changed is not relivant.
36863 * <script type="text/javascript">
36866 * @class Roo.ContentPanel
36867 * @extends Roo.util.Observable
36868 * A basic ContentPanel element.
36869 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36870 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36871 * @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
36872 * @cfg {Boolean} closable True if the panel can be closed/removed
36873 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36874 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36875 * @cfg {Toolbar} toolbar A toolbar for this panel
36876 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36877 * @cfg {String} title The title for this panel
36878 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36879 * @cfg {String} url Calls {@link #setUrl} with this value
36880 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36881 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36882 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36883 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36884 * @cfg {Boolean} badges render the badges
36887 * Create a new ContentPanel.
36888 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36889 * @param {String/Object} config A string to set only the title or a config object
36890 * @param {String} content (optional) Set the HTML content for this panel
36891 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36893 Roo.bootstrap.panel.Content = function( config){
36895 this.tpl = config.tpl || false;
36897 var el = config.el;
36898 var content = config.content;
36900 if(config.autoCreate){ // xtype is available if this is called from factory
36903 this.el = Roo.get(el);
36904 if(!this.el && config && config.autoCreate){
36905 if(typeof config.autoCreate == "object"){
36906 if(!config.autoCreate.id){
36907 config.autoCreate.id = config.id||el;
36909 this.el = Roo.DomHelper.append(document.body,
36910 config.autoCreate, true);
36912 var elcfg = { tag: "div",
36913 cls: "roo-layout-inactive-content",
36917 elcfg.html = config.html;
36921 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36924 this.closable = false;
36925 this.loaded = false;
36926 this.active = false;
36929 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36931 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36933 this.wrapEl = this.el; //this.el.wrap();
36935 if (config.toolbar.items) {
36936 ti = config.toolbar.items ;
36937 delete config.toolbar.items ;
36941 this.toolbar.render(this.wrapEl, 'before');
36942 for(var i =0;i < ti.length;i++) {
36943 // Roo.log(['add child', items[i]]);
36944 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36946 this.toolbar.items = nitems;
36947 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36948 delete config.toolbar;
36952 // xtype created footer. - not sure if will work as we normally have to render first..
36953 if (this.footer && !this.footer.el && this.footer.xtype) {
36954 if (!this.wrapEl) {
36955 this.wrapEl = this.el.wrap();
36958 this.footer.container = this.wrapEl.createChild();
36960 this.footer = Roo.factory(this.footer, Roo);
36965 if(typeof config == "string"){
36966 this.title = config;
36968 Roo.apply(this, config);
36972 this.resizeEl = Roo.get(this.resizeEl, true);
36974 this.resizeEl = this.el;
36976 // handle view.xtype
36984 * Fires when this panel is activated.
36985 * @param {Roo.ContentPanel} this
36989 * @event deactivate
36990 * Fires when this panel is activated.
36991 * @param {Roo.ContentPanel} this
36993 "deactivate" : true,
36997 * Fires when this panel is resized if fitToFrame is true.
36998 * @param {Roo.ContentPanel} this
36999 * @param {Number} width The width after any component adjustments
37000 * @param {Number} height The height after any component adjustments
37006 * Fires when this tab is created
37007 * @param {Roo.ContentPanel} this
37018 if(this.autoScroll){
37019 this.resizeEl.setStyle("overflow", "auto");
37021 // fix randome scrolling
37022 //this.el.on('scroll', function() {
37023 // Roo.log('fix random scolling');
37024 // this.scrollTo('top',0);
37027 content = content || this.content;
37029 this.setContent(content);
37031 if(config && config.url){
37032 this.setUrl(this.url, this.params, this.loadOnce);
37037 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37039 if (this.view && typeof(this.view.xtype) != 'undefined') {
37040 this.view.el = this.el.appendChild(document.createElement("div"));
37041 this.view = Roo.factory(this.view);
37042 this.view.render && this.view.render(false, '');
37046 this.fireEvent('render', this);
37049 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37053 setRegion : function(region){
37054 this.region = region;
37055 this.setActiveClass(region && !this.background);
37059 setActiveClass: function(state)
37062 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37063 this.el.setStyle('position','relative');
37065 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37066 this.el.setStyle('position', 'absolute');
37071 * Returns the toolbar for this Panel if one was configured.
37072 * @return {Roo.Toolbar}
37074 getToolbar : function(){
37075 return this.toolbar;
37078 setActiveState : function(active)
37080 this.active = active;
37081 this.setActiveClass(active);
37083 if(this.fireEvent("deactivate", this) === false){
37088 this.fireEvent("activate", this);
37092 * Updates this panel's element
37093 * @param {String} content The new content
37094 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37096 setContent : function(content, loadScripts){
37097 this.el.update(content, loadScripts);
37100 ignoreResize : function(w, h){
37101 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37104 this.lastSize = {width: w, height: h};
37109 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37110 * @return {Roo.UpdateManager} The UpdateManager
37112 getUpdateManager : function(){
37113 return this.el.getUpdateManager();
37116 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37117 * @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:
37120 url: "your-url.php",
37121 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37122 callback: yourFunction,
37123 scope: yourObject, //(optional scope)
37126 text: "Loading...",
37131 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37132 * 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.
37133 * @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}
37134 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37135 * @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.
37136 * @return {Roo.ContentPanel} this
37139 var um = this.el.getUpdateManager();
37140 um.update.apply(um, arguments);
37146 * 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.
37147 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37148 * @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)
37149 * @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)
37150 * @return {Roo.UpdateManager} The UpdateManager
37152 setUrl : function(url, params, loadOnce){
37153 if(this.refreshDelegate){
37154 this.removeListener("activate", this.refreshDelegate);
37156 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37157 this.on("activate", this.refreshDelegate);
37158 return this.el.getUpdateManager();
37161 _handleRefresh : function(url, params, loadOnce){
37162 if(!loadOnce || !this.loaded){
37163 var updater = this.el.getUpdateManager();
37164 updater.update(url, params, this._setLoaded.createDelegate(this));
37168 _setLoaded : function(){
37169 this.loaded = true;
37173 * Returns this panel's id
37176 getId : function(){
37181 * Returns this panel's element - used by regiosn to add.
37182 * @return {Roo.Element}
37184 getEl : function(){
37185 return this.wrapEl || this.el;
37190 adjustForComponents : function(width, height)
37192 //Roo.log('adjustForComponents ');
37193 if(this.resizeEl != this.el){
37194 width -= this.el.getFrameWidth('lr');
37195 height -= this.el.getFrameWidth('tb');
37198 var te = this.toolbar.getEl();
37199 te.setWidth(width);
37200 height -= te.getHeight();
37203 var te = this.footer.getEl();
37204 te.setWidth(width);
37205 height -= te.getHeight();
37209 if(this.adjustments){
37210 width += this.adjustments[0];
37211 height += this.adjustments[1];
37213 return {"width": width, "height": height};
37216 setSize : function(width, height){
37217 if(this.fitToFrame && !this.ignoreResize(width, height)){
37218 if(this.fitContainer && this.resizeEl != this.el){
37219 this.el.setSize(width, height);
37221 var size = this.adjustForComponents(width, height);
37222 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37223 this.fireEvent('resize', this, size.width, size.height);
37228 * Returns this panel's title
37231 getTitle : function(){
37233 if (typeof(this.title) != 'object') {
37238 for (var k in this.title) {
37239 if (!this.title.hasOwnProperty(k)) {
37243 if (k.indexOf('-') >= 0) {
37244 var s = k.split('-');
37245 for (var i = 0; i<s.length; i++) {
37246 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37249 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37256 * Set this panel's title
37257 * @param {String} title
37259 setTitle : function(title){
37260 this.title = title;
37262 this.region.updatePanelTitle(this, title);
37267 * Returns true is this panel was configured to be closable
37268 * @return {Boolean}
37270 isClosable : function(){
37271 return this.closable;
37274 beforeSlide : function(){
37276 this.resizeEl.clip();
37279 afterSlide : function(){
37281 this.resizeEl.unclip();
37285 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37286 * Will fail silently if the {@link #setUrl} method has not been called.
37287 * This does not activate the panel, just updates its content.
37289 refresh : function(){
37290 if(this.refreshDelegate){
37291 this.loaded = false;
37292 this.refreshDelegate();
37297 * Destroys this panel
37299 destroy : function(){
37300 this.el.removeAllListeners();
37301 var tempEl = document.createElement("span");
37302 tempEl.appendChild(this.el.dom);
37303 tempEl.innerHTML = "";
37309 * form - if the content panel contains a form - this is a reference to it.
37310 * @type {Roo.form.Form}
37314 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37315 * This contains a reference to it.
37321 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37331 * @param {Object} cfg Xtype definition of item to add.
37335 getChildContainer: function () {
37336 return this.getEl();
37341 var ret = new Roo.factory(cfg);
37346 if (cfg.xtype.match(/^Form$/)) {
37349 //if (this.footer) {
37350 // el = this.footer.container.insertSibling(false, 'before');
37352 el = this.el.createChild();
37355 this.form = new Roo.form.Form(cfg);
37358 if ( this.form.allItems.length) {
37359 this.form.render(el.dom);
37363 // should only have one of theses..
37364 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37365 // views.. should not be just added - used named prop 'view''
37367 cfg.el = this.el.appendChild(document.createElement("div"));
37370 var ret = new Roo.factory(cfg);
37372 ret.render && ret.render(false, ''); // render blank..
37382 * @class Roo.bootstrap.panel.Grid
37383 * @extends Roo.bootstrap.panel.Content
37385 * Create a new GridPanel.
37386 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37387 * @param {Object} config A the config object
37393 Roo.bootstrap.panel.Grid = function(config)
37397 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37398 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37400 config.el = this.wrapper;
37401 //this.el = this.wrapper;
37403 if (config.container) {
37404 // ctor'ed from a Border/panel.grid
37407 this.wrapper.setStyle("overflow", "hidden");
37408 this.wrapper.addClass('roo-grid-container');
37413 if(config.toolbar){
37414 var tool_el = this.wrapper.createChild();
37415 this.toolbar = Roo.factory(config.toolbar);
37417 if (config.toolbar.items) {
37418 ti = config.toolbar.items ;
37419 delete config.toolbar.items ;
37423 this.toolbar.render(tool_el);
37424 for(var i =0;i < ti.length;i++) {
37425 // Roo.log(['add child', items[i]]);
37426 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37428 this.toolbar.items = nitems;
37430 delete config.toolbar;
37433 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37434 config.grid.scrollBody = true;;
37435 config.grid.monitorWindowResize = false; // turn off autosizing
37436 config.grid.autoHeight = false;
37437 config.grid.autoWidth = false;
37439 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37441 if (config.background) {
37442 // render grid on panel activation (if panel background)
37443 this.on('activate', function(gp) {
37444 if (!gp.grid.rendered) {
37445 gp.grid.render(this.wrapper);
37446 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37451 this.grid.render(this.wrapper);
37452 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37455 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37456 // ??? needed ??? config.el = this.wrapper;
37461 // xtype created footer. - not sure if will work as we normally have to render first..
37462 if (this.footer && !this.footer.el && this.footer.xtype) {
37464 var ctr = this.grid.getView().getFooterPanel(true);
37465 this.footer.dataSource = this.grid.dataSource;
37466 this.footer = Roo.factory(this.footer, Roo);
37467 this.footer.render(ctr);
37477 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37478 getId : function(){
37479 return this.grid.id;
37483 * Returns the grid for this panel
37484 * @return {Roo.bootstrap.Table}
37486 getGrid : function(){
37490 setSize : function(width, height){
37491 if(!this.ignoreResize(width, height)){
37492 var grid = this.grid;
37493 var size = this.adjustForComponents(width, height);
37494 var gridel = grid.getGridEl();
37495 gridel.setSize(size.width, size.height);
37497 var thd = grid.getGridEl().select('thead',true).first();
37498 var tbd = grid.getGridEl().select('tbody', true).first();
37500 tbd.setSize(width, height - thd.getHeight());
37509 beforeSlide : function(){
37510 this.grid.getView().scroller.clip();
37513 afterSlide : function(){
37514 this.grid.getView().scroller.unclip();
37517 destroy : function(){
37518 this.grid.destroy();
37520 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37525 * @class Roo.bootstrap.panel.Nest
37526 * @extends Roo.bootstrap.panel.Content
37528 * Create a new Panel, that can contain a layout.Border.
37531 * @param {Roo.BorderLayout} layout The layout for this panel
37532 * @param {String/Object} config A string to set only the title or a config object
37534 Roo.bootstrap.panel.Nest = function(config)
37536 // construct with only one argument..
37537 /* FIXME - implement nicer consturctors
37538 if (layout.layout) {
37540 layout = config.layout;
37541 delete config.layout;
37543 if (layout.xtype && !layout.getEl) {
37544 // then layout needs constructing..
37545 layout = Roo.factory(layout, Roo);
37549 config.el = config.layout.getEl();
37551 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37553 config.layout.monitorWindowResize = false; // turn off autosizing
37554 this.layout = config.layout;
37555 this.layout.getEl().addClass("roo-layout-nested-layout");
37562 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37564 setSize : function(width, height){
37565 if(!this.ignoreResize(width, height)){
37566 var size = this.adjustForComponents(width, height);
37567 var el = this.layout.getEl();
37568 if (size.height < 1) {
37569 el.setWidth(size.width);
37571 el.setSize(size.width, size.height);
37573 var touch = el.dom.offsetWidth;
37574 this.layout.layout();
37575 // ie requires a double layout on the first pass
37576 if(Roo.isIE && !this.initialized){
37577 this.initialized = true;
37578 this.layout.layout();
37583 // activate all subpanels if not currently active..
37585 setActiveState : function(active){
37586 this.active = active;
37587 this.setActiveClass(active);
37590 this.fireEvent("deactivate", this);
37594 this.fireEvent("activate", this);
37595 // not sure if this should happen before or after..
37596 if (!this.layout) {
37597 return; // should not happen..
37600 for (var r in this.layout.regions) {
37601 reg = this.layout.getRegion(r);
37602 if (reg.getActivePanel()) {
37603 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37604 reg.setActivePanel(reg.getActivePanel());
37607 if (!reg.panels.length) {
37610 reg.showPanel(reg.getPanel(0));
37619 * Returns the nested BorderLayout for this panel
37620 * @return {Roo.BorderLayout}
37622 getLayout : function(){
37623 return this.layout;
37627 * Adds a xtype elements to the layout of the nested panel
37631 xtype : 'ContentPanel',
37638 xtype : 'NestedLayoutPanel',
37644 items : [ ... list of content panels or nested layout panels.. ]
37648 * @param {Object} cfg Xtype definition of item to add.
37650 addxtype : function(cfg) {
37651 return this.layout.addxtype(cfg);
37656 * Ext JS Library 1.1.1
37657 * Copyright(c) 2006-2007, Ext JS, LLC.
37659 * Originally Released Under LGPL - original licence link has changed is not relivant.
37662 * <script type="text/javascript">
37665 * @class Roo.TabPanel
37666 * @extends Roo.util.Observable
37667 * A lightweight tab container.
37671 // basic tabs 1, built from existing content
37672 var tabs = new Roo.TabPanel("tabs1");
37673 tabs.addTab("script", "View Script");
37674 tabs.addTab("markup", "View Markup");
37675 tabs.activate("script");
37677 // more advanced tabs, built from javascript
37678 var jtabs = new Roo.TabPanel("jtabs");
37679 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37681 // set up the UpdateManager
37682 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37683 var updater = tab2.getUpdateManager();
37684 updater.setDefaultUrl("ajax1.htm");
37685 tab2.on('activate', updater.refresh, updater, true);
37687 // Use setUrl for Ajax loading
37688 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37689 tab3.setUrl("ajax2.htm", null, true);
37692 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37695 jtabs.activate("jtabs-1");
37698 * Create a new TabPanel.
37699 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37700 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37702 Roo.bootstrap.panel.Tabs = function(config){
37704 * The container element for this TabPanel.
37705 * @type Roo.Element
37707 this.el = Roo.get(config.el);
37710 if(typeof config == "boolean"){
37711 this.tabPosition = config ? "bottom" : "top";
37713 Roo.apply(this, config);
37717 if(this.tabPosition == "bottom"){
37718 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37719 this.el.addClass("roo-tabs-bottom");
37721 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37722 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37723 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37725 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37727 if(this.tabPosition != "bottom"){
37728 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37729 * @type Roo.Element
37731 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37732 this.el.addClass("roo-tabs-top");
37736 this.bodyEl.setStyle("position", "relative");
37738 this.active = null;
37739 this.activateDelegate = this.activate.createDelegate(this);
37744 * Fires when the active tab changes
37745 * @param {Roo.TabPanel} this
37746 * @param {Roo.TabPanelItem} activePanel The new active tab
37750 * @event beforetabchange
37751 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37752 * @param {Roo.TabPanel} this
37753 * @param {Object} e Set cancel to true on this object to cancel the tab change
37754 * @param {Roo.TabPanelItem} tab The tab being changed to
37756 "beforetabchange" : true
37759 Roo.EventManager.onWindowResize(this.onResize, this);
37760 this.cpad = this.el.getPadding("lr");
37761 this.hiddenCount = 0;
37764 // toolbar on the tabbar support...
37765 if (this.toolbar) {
37766 alert("no toolbar support yet");
37767 this.toolbar = false;
37769 var tcfg = this.toolbar;
37770 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37771 this.toolbar = new Roo.Toolbar(tcfg);
37772 if (Roo.isSafari) {
37773 var tbl = tcfg.container.child('table', true);
37774 tbl.setAttribute('width', '100%');
37782 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37785 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37787 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37789 tabPosition : "top",
37791 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37793 currentTabWidth : 0,
37795 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37799 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37803 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37805 preferredTabWidth : 175,
37807 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37809 resizeTabs : false,
37811 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37813 monitorResize : true,
37815 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37820 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37821 * @param {String} id The id of the div to use <b>or create</b>
37822 * @param {String} text The text for the tab
37823 * @param {String} content (optional) Content to put in the TabPanelItem body
37824 * @param {Boolean} closable (optional) True to create a close icon on the tab
37825 * @return {Roo.TabPanelItem} The created TabPanelItem
37827 addTab : function(id, text, content, closable, tpl)
37829 var item = new Roo.bootstrap.panel.TabItem({
37833 closable : closable,
37836 this.addTabItem(item);
37838 item.setContent(content);
37844 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37845 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37846 * @return {Roo.TabPanelItem}
37848 getTab : function(id){
37849 return this.items[id];
37853 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37854 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37856 hideTab : function(id){
37857 var t = this.items[id];
37860 this.hiddenCount++;
37861 this.autoSizeTabs();
37866 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37867 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37869 unhideTab : function(id){
37870 var t = this.items[id];
37872 t.setHidden(false);
37873 this.hiddenCount--;
37874 this.autoSizeTabs();
37879 * Adds an existing {@link Roo.TabPanelItem}.
37880 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37882 addTabItem : function(item){
37883 this.items[item.id] = item;
37884 this.items.push(item);
37885 // if(this.resizeTabs){
37886 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37887 // this.autoSizeTabs();
37889 // item.autoSize();
37894 * Removes a {@link Roo.TabPanelItem}.
37895 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37897 removeTab : function(id){
37898 var items = this.items;
37899 var tab = items[id];
37900 if(!tab) { return; }
37901 var index = items.indexOf(tab);
37902 if(this.active == tab && items.length > 1){
37903 var newTab = this.getNextAvailable(index);
37908 this.stripEl.dom.removeChild(tab.pnode.dom);
37909 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37910 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37912 items.splice(index, 1);
37913 delete this.items[tab.id];
37914 tab.fireEvent("close", tab);
37915 tab.purgeListeners();
37916 this.autoSizeTabs();
37919 getNextAvailable : function(start){
37920 var items = this.items;
37922 // look for a next tab that will slide over to
37923 // replace the one being removed
37924 while(index < items.length){
37925 var item = items[++index];
37926 if(item && !item.isHidden()){
37930 // if one isn't found select the previous tab (on the left)
37933 var item = items[--index];
37934 if(item && !item.isHidden()){
37942 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37943 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37945 disableTab : function(id){
37946 var tab = this.items[id];
37947 if(tab && this.active != tab){
37953 * Enables a {@link Roo.TabPanelItem} that is disabled.
37954 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37956 enableTab : function(id){
37957 var tab = this.items[id];
37962 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37963 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37964 * @return {Roo.TabPanelItem} The TabPanelItem.
37966 activate : function(id){
37967 var tab = this.items[id];
37971 if(tab == this.active || tab.disabled){
37975 this.fireEvent("beforetabchange", this, e, tab);
37976 if(e.cancel !== true && !tab.disabled){
37978 this.active.hide();
37980 this.active = this.items[id];
37981 this.active.show();
37982 this.fireEvent("tabchange", this, this.active);
37988 * Gets the active {@link Roo.TabPanelItem}.
37989 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37991 getActiveTab : function(){
37992 return this.active;
37996 * Updates the tab body element to fit the height of the container element
37997 * for overflow scrolling
37998 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38000 syncHeight : function(targetHeight){
38001 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38002 var bm = this.bodyEl.getMargins();
38003 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38004 this.bodyEl.setHeight(newHeight);
38008 onResize : function(){
38009 if(this.monitorResize){
38010 this.autoSizeTabs();
38015 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38017 beginUpdate : function(){
38018 this.updating = true;
38022 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38024 endUpdate : function(){
38025 this.updating = false;
38026 this.autoSizeTabs();
38030 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38032 autoSizeTabs : function(){
38033 var count = this.items.length;
38034 var vcount = count - this.hiddenCount;
38035 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38038 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38039 var availWidth = Math.floor(w / vcount);
38040 var b = this.stripBody;
38041 if(b.getWidth() > w){
38042 var tabs = this.items;
38043 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38044 if(availWidth < this.minTabWidth){
38045 /*if(!this.sleft){ // incomplete scrolling code
38046 this.createScrollButtons();
38049 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38052 if(this.currentTabWidth < this.preferredTabWidth){
38053 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38059 * Returns the number of tabs in this TabPanel.
38062 getCount : function(){
38063 return this.items.length;
38067 * Resizes all the tabs to the passed width
38068 * @param {Number} The new width
38070 setTabWidth : function(width){
38071 this.currentTabWidth = width;
38072 for(var i = 0, len = this.items.length; i < len; i++) {
38073 if(!this.items[i].isHidden()) {
38074 this.items[i].setWidth(width);
38080 * Destroys this TabPanel
38081 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38083 destroy : function(removeEl){
38084 Roo.EventManager.removeResizeListener(this.onResize, this);
38085 for(var i = 0, len = this.items.length; i < len; i++){
38086 this.items[i].purgeListeners();
38088 if(removeEl === true){
38089 this.el.update("");
38094 createStrip : function(container)
38096 var strip = document.createElement("nav");
38097 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38098 container.appendChild(strip);
38102 createStripList : function(strip)
38104 // div wrapper for retard IE
38105 // returns the "tr" element.
38106 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38107 //'<div class="x-tabs-strip-wrap">'+
38108 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38109 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38110 return strip.firstChild; //.firstChild.firstChild.firstChild;
38112 createBody : function(container)
38114 var body = document.createElement("div");
38115 Roo.id(body, "tab-body");
38116 //Roo.fly(body).addClass("x-tabs-body");
38117 Roo.fly(body).addClass("tab-content");
38118 container.appendChild(body);
38121 createItemBody :function(bodyEl, id){
38122 var body = Roo.getDom(id);
38124 body = document.createElement("div");
38127 //Roo.fly(body).addClass("x-tabs-item-body");
38128 Roo.fly(body).addClass("tab-pane");
38129 bodyEl.insertBefore(body, bodyEl.firstChild);
38133 createStripElements : function(stripEl, text, closable, tpl)
38135 var td = document.createElement("li"); // was td..
38138 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38141 stripEl.appendChild(td);
38143 td.className = "x-tabs-closable";
38144 if(!this.closeTpl){
38145 this.closeTpl = new Roo.Template(
38146 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38147 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38148 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38151 var el = this.closeTpl.overwrite(td, {"text": text});
38152 var close = el.getElementsByTagName("div")[0];
38153 var inner = el.getElementsByTagName("em")[0];
38154 return {"el": el, "close": close, "inner": inner};
38157 // not sure what this is..
38158 // if(!this.tabTpl){
38159 //this.tabTpl = new Roo.Template(
38160 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38161 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38163 // this.tabTpl = new Roo.Template(
38164 // '<a href="#">' +
38165 // '<span unselectable="on"' +
38166 // (this.disableTooltips ? '' : ' title="{text}"') +
38167 // ' >{text}</span></a>'
38173 var template = tpl || this.tabTpl || false;
38177 template = new Roo.Template(
38179 '<span unselectable="on"' +
38180 (this.disableTooltips ? '' : ' title="{text}"') +
38181 ' >{text}</span></a>'
38185 switch (typeof(template)) {
38189 template = new Roo.Template(template);
38195 var el = template.overwrite(td, {"text": text});
38197 var inner = el.getElementsByTagName("span")[0];
38199 return {"el": el, "inner": inner};
38207 * @class Roo.TabPanelItem
38208 * @extends Roo.util.Observable
38209 * Represents an individual item (tab plus body) in a TabPanel.
38210 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38211 * @param {String} id The id of this TabPanelItem
38212 * @param {String} text The text for the tab of this TabPanelItem
38213 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38215 Roo.bootstrap.panel.TabItem = function(config){
38217 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38218 * @type Roo.TabPanel
38220 this.tabPanel = config.panel;
38222 * The id for this TabPanelItem
38225 this.id = config.id;
38227 this.disabled = false;
38229 this.text = config.text;
38231 this.loaded = false;
38232 this.closable = config.closable;
38235 * The body element for this TabPanelItem.
38236 * @type Roo.Element
38238 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38239 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38240 this.bodyEl.setStyle("display", "block");
38241 this.bodyEl.setStyle("zoom", "1");
38242 //this.hideAction();
38244 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38246 this.el = Roo.get(els.el);
38247 this.inner = Roo.get(els.inner, true);
38248 this.textEl = Roo.get(this.el.dom.firstChild, true);
38249 this.pnode = Roo.get(els.el.parentNode, true);
38250 // this.el.on("mousedown", this.onTabMouseDown, this);
38251 this.el.on("click", this.onTabClick, this);
38253 if(config.closable){
38254 var c = Roo.get(els.close, true);
38255 c.dom.title = this.closeText;
38256 c.addClassOnOver("close-over");
38257 c.on("click", this.closeClick, this);
38263 * Fires when this tab becomes the active tab.
38264 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38265 * @param {Roo.TabPanelItem} this
38269 * @event beforeclose
38270 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38271 * @param {Roo.TabPanelItem} this
38272 * @param {Object} e Set cancel to true on this object to cancel the close.
38274 "beforeclose": true,
38277 * Fires when this tab is closed.
38278 * @param {Roo.TabPanelItem} this
38282 * @event deactivate
38283 * Fires when this tab is no longer the active tab.
38284 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38285 * @param {Roo.TabPanelItem} this
38287 "deactivate" : true
38289 this.hidden = false;
38291 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38294 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38296 purgeListeners : function(){
38297 Roo.util.Observable.prototype.purgeListeners.call(this);
38298 this.el.removeAllListeners();
38301 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38304 this.pnode.addClass("active");
38307 this.tabPanel.stripWrap.repaint();
38309 this.fireEvent("activate", this.tabPanel, this);
38313 * Returns true if this tab is the active tab.
38314 * @return {Boolean}
38316 isActive : function(){
38317 return this.tabPanel.getActiveTab() == this;
38321 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38324 this.pnode.removeClass("active");
38326 this.fireEvent("deactivate", this.tabPanel, this);
38329 hideAction : function(){
38330 this.bodyEl.hide();
38331 this.bodyEl.setStyle("position", "absolute");
38332 this.bodyEl.setLeft("-20000px");
38333 this.bodyEl.setTop("-20000px");
38336 showAction : function(){
38337 this.bodyEl.setStyle("position", "relative");
38338 this.bodyEl.setTop("");
38339 this.bodyEl.setLeft("");
38340 this.bodyEl.show();
38344 * Set the tooltip for the tab.
38345 * @param {String} tooltip The tab's tooltip
38347 setTooltip : function(text){
38348 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38349 this.textEl.dom.qtip = text;
38350 this.textEl.dom.removeAttribute('title');
38352 this.textEl.dom.title = text;
38356 onTabClick : function(e){
38357 e.preventDefault();
38358 this.tabPanel.activate(this.id);
38361 onTabMouseDown : function(e){
38362 e.preventDefault();
38363 this.tabPanel.activate(this.id);
38366 getWidth : function(){
38367 return this.inner.getWidth();
38370 setWidth : function(width){
38371 var iwidth = width - this.pnode.getPadding("lr");
38372 this.inner.setWidth(iwidth);
38373 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38374 this.pnode.setWidth(width);
38378 * Show or hide the tab
38379 * @param {Boolean} hidden True to hide or false to show.
38381 setHidden : function(hidden){
38382 this.hidden = hidden;
38383 this.pnode.setStyle("display", hidden ? "none" : "");
38387 * Returns true if this tab is "hidden"
38388 * @return {Boolean}
38390 isHidden : function(){
38391 return this.hidden;
38395 * Returns the text for this tab
38398 getText : function(){
38402 autoSize : function(){
38403 //this.el.beginMeasure();
38404 this.textEl.setWidth(1);
38406 * #2804 [new] Tabs in Roojs
38407 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38409 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38410 //this.el.endMeasure();
38414 * Sets the text for the tab (Note: this also sets the tooltip text)
38415 * @param {String} text The tab's text and tooltip
38417 setText : function(text){
38419 this.textEl.update(text);
38420 this.setTooltip(text);
38421 //if(!this.tabPanel.resizeTabs){
38422 // this.autoSize();
38426 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38428 activate : function(){
38429 this.tabPanel.activate(this.id);
38433 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38435 disable : function(){
38436 if(this.tabPanel.active != this){
38437 this.disabled = true;
38438 this.pnode.addClass("disabled");
38443 * Enables this TabPanelItem if it was previously disabled.
38445 enable : function(){
38446 this.disabled = false;
38447 this.pnode.removeClass("disabled");
38451 * Sets the content for this TabPanelItem.
38452 * @param {String} content The content
38453 * @param {Boolean} loadScripts true to look for and load scripts
38455 setContent : function(content, loadScripts){
38456 this.bodyEl.update(content, loadScripts);
38460 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38461 * @return {Roo.UpdateManager} The UpdateManager
38463 getUpdateManager : function(){
38464 return this.bodyEl.getUpdateManager();
38468 * Set a URL to be used to load the content for this TabPanelItem.
38469 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38470 * @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)
38471 * @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)
38472 * @return {Roo.UpdateManager} The UpdateManager
38474 setUrl : function(url, params, loadOnce){
38475 if(this.refreshDelegate){
38476 this.un('activate', this.refreshDelegate);
38478 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38479 this.on("activate", this.refreshDelegate);
38480 return this.bodyEl.getUpdateManager();
38484 _handleRefresh : function(url, params, loadOnce){
38485 if(!loadOnce || !this.loaded){
38486 var updater = this.bodyEl.getUpdateManager();
38487 updater.update(url, params, this._setLoaded.createDelegate(this));
38492 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38493 * Will fail silently if the setUrl method has not been called.
38494 * This does not activate the panel, just updates its content.
38496 refresh : function(){
38497 if(this.refreshDelegate){
38498 this.loaded = false;
38499 this.refreshDelegate();
38504 _setLoaded : function(){
38505 this.loaded = true;
38509 closeClick : function(e){
38512 this.fireEvent("beforeclose", this, o);
38513 if(o.cancel !== true){
38514 this.tabPanel.removeTab(this.id);
38518 * The text displayed in the tooltip for the close icon.
38521 closeText : "Close this tab"
38524 * This script refer to:
38525 * Title: International Telephone Input
38526 * Author: Jack O'Connor
38527 * Code version: v12.1.12
38528 * Availability: https://github.com/jackocnr/intl-tel-input.git
38531 Roo.bootstrap.PhoneInputData = function() {
38534 "Afghanistan (افغانستان)",
38539 "Albania (Shqipëri)",
38544 "Algeria (الجزائر)",
38569 "Antigua and Barbuda",
38579 "Armenia (Հայաստան)",
38595 "Austria (Österreich)",
38600 "Azerbaijan (Azərbaycan)",
38610 "Bahrain (البحرين)",
38615 "Bangladesh (বাংলাদেশ)",
38625 "Belarus (Беларусь)",
38630 "Belgium (België)",
38660 "Bosnia and Herzegovina (Босна и Херцеговина)",
38675 "British Indian Ocean Territory",
38680 "British Virgin Islands",
38690 "Bulgaria (България)",
38700 "Burundi (Uburundi)",
38705 "Cambodia (កម្ពុជា)",
38710 "Cameroon (Cameroun)",
38719 ["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"]
38722 "Cape Verde (Kabu Verdi)",
38727 "Caribbean Netherlands",
38738 "Central African Republic (République centrafricaine)",
38758 "Christmas Island",
38764 "Cocos (Keeling) Islands",
38775 "Comoros (جزر القمر)",
38780 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38785 "Congo (Republic) (Congo-Brazzaville)",
38805 "Croatia (Hrvatska)",
38826 "Czech Republic (Česká republika)",
38831 "Denmark (Danmark)",
38846 "Dominican Republic (República Dominicana)",
38850 ["809", "829", "849"]
38868 "Equatorial Guinea (Guinea Ecuatorial)",
38888 "Falkland Islands (Islas Malvinas)",
38893 "Faroe Islands (Føroyar)",
38914 "French Guiana (Guyane française)",
38919 "French Polynesia (Polynésie française)",
38934 "Georgia (საქართველო)",
38939 "Germany (Deutschland)",
38959 "Greenland (Kalaallit Nunaat)",
38996 "Guinea-Bissau (Guiné Bissau)",
39021 "Hungary (Magyarország)",
39026 "Iceland (Ísland)",
39046 "Iraq (العراق)",
39062 "Israel (ישראל)",
39089 "Jordan (الأردن)",
39094 "Kazakhstan (Казахстан)",
39115 "Kuwait (الكويت)",
39120 "Kyrgyzstan (Кыргызстан)",
39130 "Latvia (Latvija)",
39135 "Lebanon (لبنان)",
39150 "Libya (ليبيا)",
39160 "Lithuania (Lietuva)",
39175 "Macedonia (FYROM) (Македонија)",
39180 "Madagascar (Madagasikara)",
39210 "Marshall Islands",
39220 "Mauritania (موريتانيا)",
39225 "Mauritius (Moris)",
39246 "Moldova (Republica Moldova)",
39256 "Mongolia (Монгол)",
39261 "Montenegro (Crna Gora)",
39271 "Morocco (المغرب)",
39277 "Mozambique (Moçambique)",
39282 "Myanmar (Burma) (မြန်မာ)",
39287 "Namibia (Namibië)",
39302 "Netherlands (Nederland)",
39307 "New Caledonia (Nouvelle-Calédonie)",
39342 "North Korea (조선 민주주의 인민 공화국)",
39347 "Northern Mariana Islands",
39363 "Pakistan (پاکستان)",
39373 "Palestine (فلسطين)",
39383 "Papua New Guinea",
39425 "Réunion (La Réunion)",
39431 "Romania (România)",
39447 "Saint Barthélemy",
39458 "Saint Kitts and Nevis",
39468 "Saint Martin (Saint-Martin (partie française))",
39474 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39479 "Saint Vincent and the Grenadines",
39494 "São Tomé and Príncipe (São Tomé e Príncipe)",
39499 "Saudi Arabia (المملكة العربية السعودية)",
39504 "Senegal (Sénégal)",
39534 "Slovakia (Slovensko)",
39539 "Slovenia (Slovenija)",
39549 "Somalia (Soomaaliya)",
39559 "South Korea (대한민국)",
39564 "South Sudan (جنوب السودان)",
39574 "Sri Lanka (ශ්රී ලංකාව)",
39579 "Sudan (السودان)",
39589 "Svalbard and Jan Mayen",
39600 "Sweden (Sverige)",
39605 "Switzerland (Schweiz)",
39610 "Syria (سوريا)",
39655 "Trinidad and Tobago",
39660 "Tunisia (تونس)",
39665 "Turkey (Türkiye)",
39675 "Turks and Caicos Islands",
39685 "U.S. Virgin Islands",
39695 "Ukraine (Україна)",
39700 "United Arab Emirates (الإمارات العربية المتحدة)",
39722 "Uzbekistan (Oʻzbekiston)",
39732 "Vatican City (Città del Vaticano)",
39743 "Vietnam (Việt Nam)",
39748 "Wallis and Futuna (Wallis-et-Futuna)",
39753 "Western Sahara (الصحراء الغربية)",
39759 "Yemen (اليمن)",
39783 * This script refer to:
39784 * Title: International Telephone Input
39785 * Author: Jack O'Connor
39786 * Code version: v12.1.12
39787 * Availability: https://github.com/jackocnr/intl-tel-input.git
39791 * @class Roo.bootstrap.PhoneInput
39792 * @extends Roo.bootstrap.TriggerField
39793 * An input with International dial-code selection
39795 * @cfg {String} defaultDialCode default '+852'
39796 * @cfg {Array} preferedCountries default []
39799 * Create a new PhoneInput.
39800 * @param {Object} config Configuration options
39803 Roo.bootstrap.PhoneInput = function(config) {
39804 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39807 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39809 listWidth: undefined,
39811 selectedClass: 'active',
39813 invalidClass : "has-warning",
39815 validClass: 'has-success',
39817 allowed: '0123456789',
39820 * @cfg {String} defaultDialCode The default dial code when initializing the input
39822 defaultDialCode: '+852',
39825 * @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
39827 preferedCountries: false,
39829 getAutoCreate : function()
39831 var data = Roo.bootstrap.PhoneInputData();
39832 var align = this.labelAlign || this.parentLabelAlign();
39835 this.allCountries = [];
39836 this.dialCodeMapping = [];
39838 for (var i = 0; i < data.length; i++) {
39840 this.allCountries[i] = {
39844 priority: c[3] || 0,
39845 areaCodes: c[4] || null
39847 this.dialCodeMapping[c[2]] = {
39850 priority: c[3] || 0,
39851 areaCodes: c[4] || null
39863 cls : 'form-control tel-input',
39864 autocomplete: 'new-password'
39867 var hiddenInput = {
39870 cls: 'hidden-tel-input'
39874 hiddenInput.name = this.name;
39877 if (this.disabled) {
39878 input.disabled = true;
39881 var flag_container = {
39898 cls: this.hasFeedback ? 'has-feedback' : '',
39904 cls: 'dial-code-holder',
39911 cls: 'roo-select2-container input-group',
39918 if (this.fieldLabel.length) {
39921 tooltip: 'This field is required'
39927 cls: 'control-label',
39933 html: this.fieldLabel
39936 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39942 if(this.indicatorpos == 'right') {
39943 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39950 if(align == 'left') {
39958 if(this.labelWidth > 12){
39959 label.style = "width: " + this.labelWidth + 'px';
39961 if(this.labelWidth < 13 && this.labelmd == 0){
39962 this.labelmd = this.labelWidth;
39964 if(this.labellg > 0){
39965 label.cls += ' col-lg-' + this.labellg;
39966 input.cls += ' col-lg-' + (12 - this.labellg);
39968 if(this.labelmd > 0){
39969 label.cls += ' col-md-' + this.labelmd;
39970 container.cls += ' col-md-' + (12 - this.labelmd);
39972 if(this.labelsm > 0){
39973 label.cls += ' col-sm-' + this.labelsm;
39974 container.cls += ' col-sm-' + (12 - this.labelsm);
39976 if(this.labelxs > 0){
39977 label.cls += ' col-xs-' + this.labelxs;
39978 container.cls += ' col-xs-' + (12 - this.labelxs);
39988 var settings = this;
39990 ['xs','sm','md','lg'].map(function(size){
39991 if (settings[size]) {
39992 cfg.cls += ' col-' + size + '-' + settings[size];
39996 this.store = new Roo.data.Store({
39997 proxy : new Roo.data.MemoryProxy({}),
39998 reader : new Roo.data.JsonReader({
40009 'name' : 'dialCode',
40013 'name' : 'priority',
40017 'name' : 'areaCodes',
40024 if(!this.preferedCountries) {
40025 this.preferedCountries = [
40032 var p = this.preferedCountries.reverse();
40035 for (var i = 0; i < p.length; i++) {
40036 for (var j = 0; j < this.allCountries.length; j++) {
40037 if(this.allCountries[j].iso2 == p[i]) {
40038 var t = this.allCountries[j];
40039 this.allCountries.splice(j,1);
40040 this.allCountries.unshift(t);
40046 this.store.proxy.data = {
40048 data: this.allCountries
40054 initEvents : function()
40057 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40059 this.indicator = this.indicatorEl();
40060 this.flag = this.flagEl();
40061 this.dialCodeHolder = this.dialCodeHolderEl();
40063 this.trigger = this.el.select('div.flag-box',true).first();
40064 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40069 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40070 _this.list.setWidth(lw);
40073 this.list.on('mouseover', this.onViewOver, this);
40074 this.list.on('mousemove', this.onViewMove, this);
40075 this.inputEl().on("keyup", this.onKeyUp, this);
40077 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40079 this.view = new Roo.View(this.list, this.tpl, {
40080 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40083 this.view.on('click', this.onViewClick, this);
40084 this.setValue(this.defaultDialCode);
40087 onTriggerClick : function(e)
40089 Roo.log('trigger click');
40094 if(this.isExpanded()){
40096 this.hasFocus = false;
40098 this.store.load({});
40099 this.hasFocus = true;
40104 isExpanded : function()
40106 return this.list.isVisible();
40109 collapse : function()
40111 if(!this.isExpanded()){
40115 Roo.get(document).un('mousedown', this.collapseIf, this);
40116 Roo.get(document).un('mousewheel', this.collapseIf, this);
40117 this.fireEvent('collapse', this);
40121 expand : function()
40125 if(this.isExpanded() || !this.hasFocus){
40129 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40130 this.list.setWidth(lw);
40133 this.restrictHeight();
40135 Roo.get(document).on('mousedown', this.collapseIf, this);
40136 Roo.get(document).on('mousewheel', this.collapseIf, this);
40138 this.fireEvent('expand', this);
40141 restrictHeight : function()
40143 this.list.alignTo(this.inputEl(), this.listAlign);
40144 this.list.alignTo(this.inputEl(), this.listAlign);
40147 onViewOver : function(e, t)
40149 if(this.inKeyMode){
40152 var item = this.view.findItemFromChild(t);
40155 var index = this.view.indexOf(item);
40156 this.select(index, false);
40161 onViewClick : function(view, doFocus, el, e)
40163 var index = this.view.getSelectedIndexes()[0];
40165 var r = this.store.getAt(index);
40168 this.onSelect(r, index);
40170 if(doFocus !== false && !this.blockFocus){
40171 this.inputEl().focus();
40175 onViewMove : function(e, t)
40177 this.inKeyMode = false;
40180 select : function(index, scrollIntoView)
40182 this.selectedIndex = index;
40183 this.view.select(index);
40184 if(scrollIntoView !== false){
40185 var el = this.view.getNode(index);
40187 this.list.scrollChildIntoView(el, false);
40192 createList : function()
40194 this.list = Roo.get(document.body).createChild({
40196 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40197 style: 'display:none'
40200 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40203 collapseIf : function(e)
40205 var in_combo = e.within(this.el);
40206 var in_list = e.within(this.list);
40207 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40209 if (in_combo || in_list || is_list) {
40215 onSelect : function(record, index)
40217 if(this.fireEvent('beforeselect', this, record, index) !== false){
40219 this.setFlagClass(record.data.iso2);
40220 this.setDialCode(record.data.dialCode);
40221 this.hasFocus = false;
40223 this.fireEvent('select', this, record, index);
40227 flagEl : function()
40229 var flag = this.el.select('div.flag',true).first();
40236 dialCodeHolderEl : function()
40238 var d = this.el.select('input.dial-code-holder',true).first();
40245 setDialCode : function(v)
40247 this.dialCodeHolder.dom.value = '+'+v;
40250 setFlagClass : function(n)
40252 this.flag.dom.className = 'flag '+n;
40255 getValue : function()
40257 var v = this.inputEl().getValue();
40258 if(this.dialCodeHolder) {
40259 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40264 setValue : function(v)
40266 var d = this.getDialCode(v);
40268 //invalid dial code
40269 if(v.length == 0 || !d || d.length == 0) {
40271 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40272 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40278 this.setFlagClass(this.dialCodeMapping[d].iso2);
40279 this.setDialCode(d);
40280 this.inputEl().dom.value = v.replace('+'+d,'');
40281 this.hiddenEl().dom.value = this.getValue();
40286 getDialCode : function(v)
40290 if (v.length == 0) {
40291 return this.dialCodeHolder.dom.value;
40295 if (v.charAt(0) != "+") {
40298 var numericChars = "";
40299 for (var i = 1; i < v.length; i++) {
40300 var c = v.charAt(i);
40303 if (this.dialCodeMapping[numericChars]) {
40304 dialCode = v.substr(1, i);
40306 if (numericChars.length == 4) {
40316 this.setValue(this.defaultDialCode);
40320 hiddenEl : function()
40322 return this.el.select('input.hidden-tel-input',true).first();
40325 onKeyUp : function(e){
40327 var k = e.getKey();
40328 var c = e.getCharCode();
40331 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40332 this.allowed.indexOf(String.fromCharCode(c)) === -1
40337 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40340 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40344 this.setValue(this.getValue());
40349 * @class Roo.bootstrap.MoneyField
40350 * @extends Roo.bootstrap.ComboBox
40351 * Bootstrap MoneyField class
40354 * Create a new MoneyField.
40355 * @param {Object} config Configuration options
40358 Roo.bootstrap.MoneyField = function(config) {
40360 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40364 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40367 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40369 allowDecimals : true,
40371 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40373 decimalSeparator : ".",
40375 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40377 decimalPrecision : 0,
40379 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40381 allowNegative : true,
40383 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40387 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40389 minValue : Number.NEGATIVE_INFINITY,
40391 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40393 maxValue : Number.MAX_VALUE,
40395 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40397 minText : "The minimum value for this field is {0}",
40399 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40401 maxText : "The maximum value for this field is {0}",
40403 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40404 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40406 nanText : "{0} is not a valid number",
40408 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40412 * @cfg {String} defaults currency of the MoneyField
40413 * value should be in lkey
40415 defaultCurrency : false,
40417 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40419 thousandsDelimiter : false,
40429 getAutoCreate : function()
40431 var align = this.labelAlign || this.parentLabelAlign();
40443 cls : 'form-control roo-money-amount-input',
40444 autocomplete: 'new-password'
40447 var hiddenInput = {
40451 cls: 'hidden-number-input'
40455 hiddenInput.name = this.name;
40458 if (this.disabled) {
40459 input.disabled = true;
40462 var clg = 12 - this.inputlg;
40463 var cmd = 12 - this.inputmd;
40464 var csm = 12 - this.inputsm;
40465 var cxs = 12 - this.inputxs;
40469 cls : 'row roo-money-field',
40473 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40477 cls: 'roo-select2-container input-group',
40481 cls : 'form-control roo-money-currency-input',
40482 autocomplete: 'new-password',
40484 name : this.currencyName
40488 cls : 'input-group-addon',
40502 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40506 cls: this.hasFeedback ? 'has-feedback' : '',
40517 if (this.fieldLabel.length) {
40520 tooltip: 'This field is required'
40526 cls: 'control-label',
40532 html: this.fieldLabel
40535 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40541 if(this.indicatorpos == 'right') {
40542 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40549 if(align == 'left') {
40557 if(this.labelWidth > 12){
40558 label.style = "width: " + this.labelWidth + 'px';
40560 if(this.labelWidth < 13 && this.labelmd == 0){
40561 this.labelmd = this.labelWidth;
40563 if(this.labellg > 0){
40564 label.cls += ' col-lg-' + this.labellg;
40565 input.cls += ' col-lg-' + (12 - this.labellg);
40567 if(this.labelmd > 0){
40568 label.cls += ' col-md-' + this.labelmd;
40569 container.cls += ' col-md-' + (12 - this.labelmd);
40571 if(this.labelsm > 0){
40572 label.cls += ' col-sm-' + this.labelsm;
40573 container.cls += ' col-sm-' + (12 - this.labelsm);
40575 if(this.labelxs > 0){
40576 label.cls += ' col-xs-' + this.labelxs;
40577 container.cls += ' col-xs-' + (12 - this.labelxs);
40588 var settings = this;
40590 ['xs','sm','md','lg'].map(function(size){
40591 if (settings[size]) {
40592 cfg.cls += ' col-' + size + '-' + settings[size];
40599 initEvents : function()
40601 this.indicator = this.indicatorEl();
40603 this.initCurrencyEvent();
40605 this.initNumberEvent();
40608 initCurrencyEvent : function()
40611 throw "can not find store for combo";
40614 this.store = Roo.factory(this.store, Roo.data);
40615 this.store.parent = this;
40619 this.triggerEl = this.el.select('.input-group-addon', true).first();
40621 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40626 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40627 _this.list.setWidth(lw);
40630 this.list.on('mouseover', this.onViewOver, this);
40631 this.list.on('mousemove', this.onViewMove, this);
40632 this.list.on('scroll', this.onViewScroll, this);
40635 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40638 this.view = new Roo.View(this.list, this.tpl, {
40639 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40642 this.view.on('click', this.onViewClick, this);
40644 this.store.on('beforeload', this.onBeforeLoad, this);
40645 this.store.on('load', this.onLoad, this);
40646 this.store.on('loadexception', this.onLoadException, this);
40648 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40649 "up" : function(e){
40650 this.inKeyMode = true;
40654 "down" : function(e){
40655 if(!this.isExpanded()){
40656 this.onTriggerClick();
40658 this.inKeyMode = true;
40663 "enter" : function(e){
40666 if(this.fireEvent("specialkey", this, e)){
40667 this.onViewClick(false);
40673 "esc" : function(e){
40677 "tab" : function(e){
40680 if(this.fireEvent("specialkey", this, e)){
40681 this.onViewClick(false);
40689 doRelay : function(foo, bar, hname){
40690 if(hname == 'down' || this.scope.isExpanded()){
40691 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40699 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40703 initNumberEvent : function(e)
40705 this.inputEl().on("keydown" , this.fireKey, this);
40706 this.inputEl().on("focus", this.onFocus, this);
40707 this.inputEl().on("blur", this.onBlur, this);
40709 this.inputEl().relayEvent('keyup', this);
40711 if(this.indicator){
40712 this.indicator.addClass('invisible');
40715 this.originalValue = this.getValue();
40717 if(this.validationEvent == 'keyup'){
40718 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40719 this.inputEl().on('keyup', this.filterValidation, this);
40721 else if(this.validationEvent !== false){
40722 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40725 if(this.selectOnFocus){
40726 this.on("focus", this.preFocus, this);
40729 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40730 this.inputEl().on("keypress", this.filterKeys, this);
40732 this.inputEl().relayEvent('keypress', this);
40735 var allowed = "0123456789";
40737 if(this.allowDecimals){
40738 allowed += this.decimalSeparator;
40741 if(this.allowNegative){
40745 if(this.thousandsDelimiter) {
40749 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40751 var keyPress = function(e){
40753 var k = e.getKey();
40755 var c = e.getCharCode();
40758 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40759 allowed.indexOf(String.fromCharCode(c)) === -1
40765 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40769 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40774 this.inputEl().on("keypress", keyPress, this);
40778 onTriggerClick : function(e)
40785 this.loadNext = false;
40787 if(this.isExpanded()){
40792 this.hasFocus = true;
40794 if(this.triggerAction == 'all') {
40795 this.doQuery(this.allQuery, true);
40799 this.doQuery(this.getRawValue());
40802 getCurrency : function()
40804 var v = this.currencyEl().getValue();
40809 restrictHeight : function()
40811 this.list.alignTo(this.currencyEl(), this.listAlign);
40812 this.list.alignTo(this.currencyEl(), this.listAlign);
40815 onViewClick : function(view, doFocus, el, e)
40817 var index = this.view.getSelectedIndexes()[0];
40819 var r = this.store.getAt(index);
40822 this.onSelect(r, index);
40826 onSelect : function(record, index){
40828 if(this.fireEvent('beforeselect', this, record, index) !== false){
40830 this.setFromCurrencyData(index > -1 ? record.data : false);
40834 this.fireEvent('select', this, record, index);
40838 setFromCurrencyData : function(o)
40842 this.lastCurrency = o;
40844 if (this.currencyField) {
40845 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40847 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40850 this.lastSelectionText = currency;
40852 //setting default currency
40853 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40854 this.setCurrency(this.defaultCurrency);
40858 this.setCurrency(currency);
40861 setFromData : function(o)
40865 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40867 this.setFromCurrencyData(c);
40872 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40874 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40877 this.setValue(value);
40881 setCurrency : function(v)
40883 this.currencyValue = v;
40886 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40891 setValue : function(v)
40893 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40899 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40901 this.inputEl().dom.value = (v == '') ? '' :
40902 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40904 if(!this.allowZero && v === '0') {
40905 this.hiddenEl().dom.value = '';
40906 this.inputEl().dom.value = '';
40913 getRawValue : function()
40915 var v = this.inputEl().getValue();
40920 getValue : function()
40922 return this.fixPrecision(this.parseValue(this.getRawValue()));
40925 parseValue : function(value)
40927 if(this.thousandsDelimiter) {
40929 r = new RegExp(",", "g");
40930 value = value.replace(r, "");
40933 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40934 return isNaN(value) ? '' : value;
40938 fixPrecision : function(value)
40940 if(this.thousandsDelimiter) {
40942 r = new RegExp(",", "g");
40943 value = value.replace(r, "");
40946 var nan = isNaN(value);
40948 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40949 return nan ? '' : value;
40951 return parseFloat(value).toFixed(this.decimalPrecision);
40954 decimalPrecisionFcn : function(v)
40956 return Math.floor(v);
40959 validateValue : function(value)
40961 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40965 var num = this.parseValue(value);
40968 this.markInvalid(String.format(this.nanText, value));
40972 if(num < this.minValue){
40973 this.markInvalid(String.format(this.minText, this.minValue));
40977 if(num > this.maxValue){
40978 this.markInvalid(String.format(this.maxText, this.maxValue));
40985 validate : function()
40987 if(this.disabled || this.allowBlank){
40992 var currency = this.getCurrency();
40994 if(this.validateValue(this.getRawValue()) && currency.length){
40999 this.markInvalid();
41003 getName: function()
41008 beforeBlur : function()
41014 var v = this.parseValue(this.getRawValue());
41021 onBlur : function()
41025 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41026 //this.el.removeClass(this.focusClass);
41029 this.hasFocus = false;
41031 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41035 var v = this.getValue();
41037 if(String(v) !== String(this.startValue)){
41038 this.fireEvent('change', this, v, this.startValue);
41041 this.fireEvent("blur", this);
41044 inputEl : function()
41046 return this.el.select('.roo-money-amount-input', true).first();
41049 currencyEl : function()
41051 return this.el.select('.roo-money-currency-input', true).first();
41054 hiddenEl : function()
41056 return this.el.select('input.hidden-number-input',true).first();