4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
21 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
24 * Do not use directly - it does not do anything..
25 * @param {Object} config The config object
30 Roo.bootstrap.Component = function(config){
31 Roo.bootstrap.Component.superclass.constructor.call(this, config);
35 * @event childrenrendered
36 * Fires when the children have been rendered..
37 * @param {Roo.bootstrap.Component} this
39 "childrenrendered" : true
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
51 allowDomMove : false, // to stop relocations in parent onRender...
61 * Initialize Events for the element
63 initEvents : function() { },
69 can_build_overlaid : true,
71 container_method : false,
78 // returns the parent component..
79 return Roo.ComponentMgr.get(this.parentId)
85 onRender : function(ct, position)
87 // Roo.log("Call onRender: " + this.xtype);
89 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
92 if (this.el.attr('xtype')) {
93 this.el.attr('xtypex', this.el.attr('xtype'));
94 this.el.dom.removeAttribute('xtype');
104 var cfg = Roo.apply({}, this.getAutoCreate());
106 cfg.id = this.id || Roo.id();
108 // fill in the extra attributes
109 if (this.xattr && typeof(this.xattr) =='object') {
110 for (var i in this.xattr) {
111 cfg[i] = this.xattr[i];
116 cfg.dataId = this.dataId;
120 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
123 if (this.style) { // fixme needs to support more complex style data.
124 cfg.style = this.style;
128 cfg.name = this.name;
131 this.el = ct.createChild(cfg, position);
134 this.tooltipEl().attr('tooltip', this.tooltip);
137 if(this.tabIndex !== undefined){
138 this.el.dom.setAttribute('tabIndex', this.tabIndex);
145 * Fetch the element to add children to
146 * @return {Roo.Element} defaults to this.el
148 getChildContainer : function()
153 * Fetch the element to display the tooltip on.
154 * @return {Roo.Element} defaults to this.el
156 tooltipEl : function()
161 addxtype : function(tree,cntr)
165 cn = Roo.factory(tree);
166 //Roo.log(['addxtype', cn]);
168 cn.parentType = this.xtype; //??
169 cn.parentId = this.id;
171 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172 if (typeof(cn.container_method) == 'string') {
173 cntr = cn.container_method;
177 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
179 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
181 var build_from_html = Roo.XComponent.build_from_html;
183 var is_body = (tree.xtype == 'Body') ;
185 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
187 var self_cntr_el = Roo.get(this[cntr](false));
189 // do not try and build conditional elements
190 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
194 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196 return this.addxtypeChild(tree,cntr, is_body);
199 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
202 return this.addxtypeChild(Roo.apply({}, tree),cntr);
205 Roo.log('skipping render');
211 if (!build_from_html) {
215 // this i think handles overlaying multiple children of the same type
216 // with the sam eelement.. - which might be buggy..
218 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
224 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
228 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
235 addxtypeChild : function (tree, cntr, is_body)
237 Roo.debug && Roo.log('addxtypeChild:' + cntr);
239 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
242 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243 (typeof(tree['flexy:foreach']) != 'undefined');
247 skip_children = false;
248 // render the element if it's not BODY.
251 // if parent was disabled, then do not try and create the children..
252 if(!this[cntr](true)){
257 cn = Roo.factory(tree);
259 cn.parentType = this.xtype; //??
260 cn.parentId = this.id;
262 var build_from_html = Roo.XComponent.build_from_html;
265 // does the container contain child eleemnts with 'xtype' attributes.
266 // that match this xtype..
267 // note - when we render we create these as well..
268 // so we should check to see if body has xtype set.
269 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
271 var self_cntr_el = Roo.get(this[cntr](false));
272 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
274 //Roo.log(Roo.XComponent.build_from_html);
275 //Roo.log("got echild:");
278 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279 // and are not displayed -this causes this to use up the wrong element when matching.
280 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
283 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
290 //echild.dom.removeAttribute('xtype');
292 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293 Roo.debug && Roo.log(self_cntr_el);
294 Roo.debug && Roo.log(echild);
295 Roo.debug && Roo.log(cn);
301 // if object has flexy:if - then it may or may not be rendered.
302 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
303 // skip a flexy if element.
304 Roo.debug && Roo.log('skipping render');
305 Roo.debug && Roo.log(tree);
307 Roo.debug && Roo.log('skipping all children');
308 skip_children = true;
313 // actually if flexy:foreach is found, we really want to create
314 // multiple copies here...
316 //Roo.log(this[cntr]());
317 // some elements do not have render methods.. like the layouts...
319 if(this[cntr](true) === false){
324 cn.render && cn.render(this[cntr](true));
327 // then add the element..
334 if (typeof (tree.menu) != 'undefined') {
335 tree.menu.parentType = cn.xtype;
336 tree.menu.triggerEl = cn.el;
337 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
341 if (!tree.items || !tree.items.length) {
343 //Roo.log(["no children", this]);
348 var items = tree.items;
351 //Roo.log(items.length);
353 if (!skip_children) {
354 for(var i =0;i < items.length;i++) {
355 // Roo.log(['add child', items[i]]);
356 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
362 //Roo.log("fire childrenrendered");
364 cn.fireEvent('childrenrendered', this);
370 * Set the element that will be used to show or hide
372 setVisibilityEl : function(el)
374 this.visibilityEl = el;
378 * Get the element that will be used to show or hide
380 getVisibilityEl : function()
382 if (typeof(this.visibilityEl) == 'object') {
383 return this.visibilityEl;
386 if (typeof(this.visibilityEl) == 'string') {
387 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
394 * Show a component - removes 'hidden' class
398 if(!this.getVisibilityEl()){
402 this.getVisibilityEl().removeClass('hidden');
404 this.fireEvent('show', this);
409 * Hide a component - adds 'hidden' class
413 if(!this.getVisibilityEl()){
417 this.getVisibilityEl().addClass('hidden');
419 this.fireEvent('hide', this);
432 * @class Roo.bootstrap.Body
433 * @extends Roo.bootstrap.Component
434 * Bootstrap Body class
438 * @param {Object} config The config object
441 Roo.bootstrap.Body = function(config){
443 config = config || {};
445 Roo.bootstrap.Body.superclass.constructor.call(this, config);
446 this.el = Roo.get(config.el ? config.el : document.body );
447 if (this.cls && this.cls.length) {
448 Roo.get(document.body).addClass(this.cls);
452 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
454 is_body : true,// just to make sure it's constructed?
459 onRender : function(ct, position)
461 /* Roo.log("Roo.bootstrap.Body - onRender");
462 if (this.cls && this.cls.length) {
463 Roo.get(document.body).addClass(this.cls);
482 * @class Roo.bootstrap.ButtonGroup
483 * @extends Roo.bootstrap.Component
484 * Bootstrap ButtonGroup class
485 * @cfg {String} size lg | sm | xs (default empty normal)
486 * @cfg {String} align vertical | justified (default none)
487 * @cfg {String} direction up | down (default down)
488 * @cfg {Boolean} toolbar false | true
489 * @cfg {Boolean} btn true | false
494 * @param {Object} config The config object
497 Roo.bootstrap.ButtonGroup = function(config){
498 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
501 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
509 getAutoCreate : function(){
515 cfg.html = this.html || cfg.html;
526 if (['vertical','justified'].indexOf(this.align)!==-1) {
527 cfg.cls = 'btn-group-' + this.align;
529 if (this.align == 'justified') {
530 console.log(this.items);
534 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
535 cfg.cls += ' btn-group-' + this.size;
538 if (this.direction == 'up') {
539 cfg.cls += ' dropup' ;
555 * @class Roo.bootstrap.Button
556 * @extends Roo.bootstrap.Component
557 * Bootstrap Button class
558 * @cfg {String} html The button content
559 * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default
560 * @cfg {String} size ( lg | sm | xs)
561 * @cfg {String} tag ( a | input | submit)
562 * @cfg {String} href empty or href
563 * @cfg {Boolean} disabled default false;
564 * @cfg {Boolean} isClose default false;
565 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
566 * @cfg {String} badge text for badge
567 * @cfg {String} theme (default|glow)
568 * @cfg {Boolean} inverse dark themed version
569 * @cfg {Boolean} toggle is it a slidy toggle button
570 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
571 * @cfg {String} ontext text for on slidy toggle state
572 * @cfg {String} offtext text for off slidy toggle state
573 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
574 * @cfg {Boolean} removeClass remove the standard class..
575 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
578 * Create a new button
579 * @param {Object} config The config object
583 Roo.bootstrap.Button = function(config){
584 Roo.bootstrap.Button.superclass.constructor.call(this, config);
585 this.weightClass = ["btn-default",
597 * When a butotn is pressed
598 * @param {Roo.bootstrap.Button} btn
599 * @param {Roo.EventObject} e
604 * After the button has been toggles
605 * @param {Roo.bootstrap.Button} btn
606 * @param {Roo.EventObject} e
607 * @param {boolean} pressed (also available as button.pressed)
613 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
631 preventDefault: true,
639 getAutoCreate : function(){
647 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
648 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
653 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
655 if (this.toggle == true) {
658 cls: 'slider-frame roo-button',
663 'data-off-text':'OFF',
664 cls: 'slider-button',
670 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
671 cfg.cls += ' '+this.weight;
680 cfg["aria-hidden"] = true;
682 cfg.html = "×";
688 if (this.theme==='default') {
689 cfg.cls = 'btn roo-button';
691 //if (this.parentType != 'Navbar') {
692 this.weight = this.weight.length ? this.weight : 'default';
694 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
696 cfg.cls += ' btn-' + this.weight;
698 } else if (this.theme==='glow') {
701 cfg.cls = 'btn-glow roo-button';
703 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
705 cfg.cls += ' ' + this.weight;
711 this.cls += ' inverse';
715 if (this.active || this.pressed === true) {
716 cfg.cls += ' active';
720 cfg.disabled = 'disabled';
724 Roo.log('changing to ul' );
726 this.glyphicon = 'caret';
729 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
731 //gsRoo.log(this.parentType);
732 if (this.parentType === 'Navbar' && !this.parent().bar) {
733 Roo.log('changing to li?');
742 href : this.href || '#'
745 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
746 cfg.cls += ' dropdown';
753 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
755 if (this.glyphicon) {
756 cfg.html = ' ' + cfg.html;
761 cls: 'glyphicon glyphicon-' + this.glyphicon
771 // cfg.cls='btn roo-button';
775 var value = cfg.html;
780 cls: 'glyphicon glyphicon-' + this.glyphicon,
799 cfg.cls += ' dropdown';
800 cfg.html = typeof(cfg.html) != 'undefined' ?
801 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
804 if (cfg.tag !== 'a' && this.href !== '') {
805 throw "Tag must be a to set href.";
806 } else if (this.href.length > 0) {
807 cfg.href = this.href;
810 if(this.removeClass){
815 cfg.target = this.target;
820 initEvents: function() {
821 // Roo.log('init events?');
822 // Roo.log(this.el.dom);
825 if (typeof (this.menu) != 'undefined') {
826 this.menu.parentType = this.xtype;
827 this.menu.triggerEl = this.el;
828 this.addxtype(Roo.apply({}, this.menu));
832 if (this.el.hasClass('roo-button')) {
833 this.el.on('click', this.onClick, this);
835 this.el.select('.roo-button').on('click', this.onClick, this);
838 if(this.removeClass){
839 this.el.on('click', this.onClick, this);
842 this.el.enableDisplayMode();
845 onClick : function(e)
851 Roo.log('button on click ');
852 if(this.preventDefault){
856 if (this.pressed === true || this.pressed === false) {
857 this.toggleActive(e);
861 this.fireEvent('click', this, e);
865 * Enables this button
869 this.disabled = false;
870 this.el.removeClass('disabled');
874 * Disable this button
878 this.disabled = true;
879 this.el.addClass('disabled');
882 * sets the active state on/off,
883 * @param {Boolean} state (optional) Force a particular state
885 setActive : function(v) {
887 this.el[v ? 'addClass' : 'removeClass']('active');
891 * toggles the current active state
893 toggleActive : function(e)
895 this.setActive(!this.pressed);
896 this.fireEvent('toggle', this, e, !this.pressed);
899 * get the current active state
900 * @return {boolean} true if it's active
902 isActive : function()
904 return this.el.hasClass('active');
907 * set the text of the first selected button
909 setText : function(str)
911 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
914 * get the text of the first selected button
918 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
921 setWeight : function(str)
923 this.el.removeClass(this.weightClass);
924 this.el.addClass('btn-' + str);
938 * @class Roo.bootstrap.Column
939 * @extends Roo.bootstrap.Component
940 * Bootstrap Column class
941 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
942 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
943 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
944 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
945 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
946 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
947 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
948 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
951 * @cfg {Boolean} hidden (true|false) hide the element
952 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
953 * @cfg {String} fa (ban|check|...) font awesome icon
954 * @cfg {Number} fasize (1|2|....) font awsome size
956 * @cfg {String} icon (info-sign|check|...) glyphicon name
958 * @cfg {String} html content of column.
961 * Create a new Column
962 * @param {Object} config The config object
965 Roo.bootstrap.Column = function(config){
966 Roo.bootstrap.Column.superclass.constructor.call(this, config);
969 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
987 getAutoCreate : function(){
988 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
996 ['xs','sm','md','lg'].map(function(size){
997 //Roo.log( size + ':' + settings[size]);
999 if (settings[size+'off'] !== false) {
1000 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1003 if (settings[size] === false) {
1007 if (!settings[size]) { // 0 = hidden
1008 cfg.cls += ' hidden-' + size;
1011 cfg.cls += ' col-' + size + '-' + settings[size];
1016 cfg.cls += ' hidden';
1019 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1020 cfg.cls +=' alert alert-' + this.alert;
1024 if (this.html.length) {
1025 cfg.html = this.html;
1029 if (this.fasize > 1) {
1030 fasize = ' fa-' + this.fasize + 'x';
1032 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1037 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1056 * @class Roo.bootstrap.Container
1057 * @extends Roo.bootstrap.Component
1058 * Bootstrap Container class
1059 * @cfg {Boolean} jumbotron is it a jumbotron element
1060 * @cfg {String} html content of element
1061 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1062 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1063 * @cfg {String} header content of header (for panel)
1064 * @cfg {String} footer content of footer (for panel)
1065 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1066 * @cfg {String} tag (header|aside|section) type of HTML tag.
1067 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1068 * @cfg {String} fa font awesome icon
1069 * @cfg {String} icon (info-sign|check|...) glyphicon name
1070 * @cfg {Boolean} hidden (true|false) hide the element
1071 * @cfg {Boolean} expandable (true|false) default false
1072 * @cfg {Boolean} expanded (true|false) default true
1073 * @cfg {String} rheader contet on the right of header
1074 * @cfg {Boolean} clickable (true|false) default false
1078 * Create a new Container
1079 * @param {Object} config The config object
1082 Roo.bootstrap.Container = function(config){
1083 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1089 * After the panel has been expand
1091 * @param {Roo.bootstrap.Container} this
1096 * After the panel has been collapsed
1098 * @param {Roo.bootstrap.Container} this
1103 * When a element is chick
1104 * @param {Roo.bootstrap.Container} this
1105 * @param {Roo.EventObject} e
1111 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1129 getChildContainer : function() {
1135 if (this.panel.length) {
1136 return this.el.select('.panel-body',true).first();
1143 getAutoCreate : function(){
1146 tag : this.tag || 'div',
1150 if (this.jumbotron) {
1151 cfg.cls = 'jumbotron';
1156 // - this is applied by the parent..
1158 // cfg.cls = this.cls + '';
1161 if (this.sticky.length) {
1163 var bd = Roo.get(document.body);
1164 if (!bd.hasClass('bootstrap-sticky')) {
1165 bd.addClass('bootstrap-sticky');
1166 Roo.select('html',true).setStyle('height', '100%');
1169 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1173 if (this.well.length) {
1174 switch (this.well) {
1177 cfg.cls +=' well well-' +this.well;
1186 cfg.cls += ' hidden';
1190 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1191 cfg.cls +=' alert alert-' + this.alert;
1196 if (this.panel.length) {
1197 cfg.cls += ' panel panel-' + this.panel;
1199 if (this.header.length) {
1203 if(this.expandable){
1205 cfg.cls = cfg.cls + ' expandable';
1209 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1217 cls : 'panel-title',
1218 html : (this.expandable ? ' ' : '') + this.header
1222 cls: 'panel-header-right',
1228 cls : 'panel-heading',
1229 style : this.expandable ? 'cursor: pointer' : '',
1237 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1242 if (this.footer.length) {
1244 cls : 'panel-footer',
1253 body.html = this.html || cfg.html;
1254 // prefix with the icons..
1256 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1259 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1264 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1265 cfg.cls = 'container';
1271 initEvents: function()
1273 if(this.expandable){
1274 var headerEl = this.headerEl();
1277 headerEl.on('click', this.onToggleClick, this);
1282 this.el.on('click', this.onClick, this);
1287 onToggleClick : function()
1289 var headerEl = this.headerEl();
1305 if(this.fireEvent('expand', this)) {
1307 this.expanded = true;
1309 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1311 this.el.select('.panel-body',true).first().removeClass('hide');
1313 var toggleEl = this.toggleEl();
1319 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1324 collapse : function()
1326 if(this.fireEvent('collapse', this)) {
1328 this.expanded = false;
1330 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1331 this.el.select('.panel-body',true).first().addClass('hide');
1333 var toggleEl = this.toggleEl();
1339 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1343 toggleEl : function()
1345 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1349 return this.el.select('.panel-heading .fa',true).first();
1352 headerEl : function()
1354 if(!this.el || !this.panel.length || !this.header.length){
1358 return this.el.select('.panel-heading',true).first()
1363 if(!this.el || !this.panel.length){
1367 return this.el.select('.panel-body',true).first()
1370 titleEl : function()
1372 if(!this.el || !this.panel.length || !this.header.length){
1376 return this.el.select('.panel-title',true).first();
1379 setTitle : function(v)
1381 var titleEl = this.titleEl();
1387 titleEl.dom.innerHTML = v;
1390 getTitle : function()
1393 var titleEl = this.titleEl();
1399 return titleEl.dom.innerHTML;
1402 setRightTitle : function(v)
1404 var t = this.el.select('.panel-header-right',true).first();
1410 t.dom.innerHTML = v;
1413 onClick : function(e)
1417 this.fireEvent('click', this, e);
1430 * @class Roo.bootstrap.Img
1431 * @extends Roo.bootstrap.Component
1432 * Bootstrap Img class
1433 * @cfg {Boolean} imgResponsive false | true
1434 * @cfg {String} border rounded | circle | thumbnail
1435 * @cfg {String} src image source
1436 * @cfg {String} alt image alternative text
1437 * @cfg {String} href a tag href
1438 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1439 * @cfg {String} xsUrl xs image source
1440 * @cfg {String} smUrl sm image source
1441 * @cfg {String} mdUrl md image source
1442 * @cfg {String} lgUrl lg image source
1445 * Create a new Input
1446 * @param {Object} config The config object
1449 Roo.bootstrap.Img = function(config){
1450 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1456 * The img click event for the img.
1457 * @param {Roo.EventObject} e
1463 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1465 imgResponsive: true,
1475 getAutoCreate : function()
1477 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1478 return this.createSingleImg();
1483 cls: 'roo-image-responsive-group',
1488 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1490 if(!_this[size + 'Url']){
1496 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1497 html: _this.html || cfg.html,
1498 src: _this[size + 'Url']
1501 img.cls += ' roo-image-responsive-' + size;
1503 var s = ['xs', 'sm', 'md', 'lg'];
1505 s.splice(s.indexOf(size), 1);
1507 Roo.each(s, function(ss){
1508 img.cls += ' hidden-' + ss;
1511 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1512 cfg.cls += ' img-' + _this.border;
1516 cfg.alt = _this.alt;
1529 a.target = _this.target;
1533 cfg.cn.push((_this.href) ? a : img);
1540 createSingleImg : function()
1544 cls: (this.imgResponsive) ? 'img-responsive' : '',
1546 src : 'about:blank' // just incase src get's set to undefined?!?
1549 cfg.html = this.html || cfg.html;
1551 cfg.src = this.src || cfg.src;
1553 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1554 cfg.cls += ' img-' + this.border;
1571 a.target = this.target;
1576 return (this.href) ? a : cfg;
1579 initEvents: function()
1582 this.el.on('click', this.onClick, this);
1587 onClick : function(e)
1589 Roo.log('img onclick');
1590 this.fireEvent('click', this, e);
1593 * Sets the url of the image - used to update it
1594 * @param {String} url the url of the image
1597 setSrc : function(url)
1601 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1602 this.el.dom.src = url;
1606 this.el.select('img', true).first().dom.src = url;
1622 * @class Roo.bootstrap.Link
1623 * @extends Roo.bootstrap.Component
1624 * Bootstrap Link Class
1625 * @cfg {String} alt image alternative text
1626 * @cfg {String} href a tag href
1627 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1628 * @cfg {String} html the content of the link.
1629 * @cfg {String} anchor name for the anchor link
1630 * @cfg {String} fa - favicon
1632 * @cfg {Boolean} preventDefault (true | false) default false
1636 * Create a new Input
1637 * @param {Object} config The config object
1640 Roo.bootstrap.Link = function(config){
1641 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1647 * The img click event for the img.
1648 * @param {Roo.EventObject} e
1654 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1658 preventDefault: false,
1664 getAutoCreate : function()
1666 var html = this.html || '';
1668 if (this.fa !== false) {
1669 html = '<i class="fa fa-' + this.fa + '"></i>';
1674 // anchor's do not require html/href...
1675 if (this.anchor === false) {
1677 cfg.href = this.href || '#';
1679 cfg.name = this.anchor;
1680 if (this.html !== false || this.fa !== false) {
1683 if (this.href !== false) {
1684 cfg.href = this.href;
1688 if(this.alt !== false){
1693 if(this.target !== false) {
1694 cfg.target = this.target;
1700 initEvents: function() {
1702 if(!this.href || this.preventDefault){
1703 this.el.on('click', this.onClick, this);
1707 onClick : function(e)
1709 if(this.preventDefault){
1712 //Roo.log('img onclick');
1713 this.fireEvent('click', this, e);
1726 * @class Roo.bootstrap.Header
1727 * @extends Roo.bootstrap.Component
1728 * Bootstrap Header class
1729 * @cfg {String} html content of header
1730 * @cfg {Number} level (1|2|3|4|5|6) default 1
1733 * Create a new Header
1734 * @param {Object} config The config object
1738 Roo.bootstrap.Header = function(config){
1739 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1742 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1750 getAutoCreate : function(){
1755 tag: 'h' + (1 *this.level),
1756 html: this.html || ''
1768 * Ext JS Library 1.1.1
1769 * Copyright(c) 2006-2007, Ext JS, LLC.
1771 * Originally Released Under LGPL - original licence link has changed is not relivant.
1774 * <script type="text/javascript">
1778 * @class Roo.bootstrap.MenuMgr
1779 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1782 Roo.bootstrap.MenuMgr = function(){
1783 var menus, active, groups = {}, attached = false, lastShow = new Date();
1785 // private - called when first menu is created
1788 active = new Roo.util.MixedCollection();
1789 Roo.get(document).addKeyListener(27, function(){
1790 if(active.length > 0){
1798 if(active && active.length > 0){
1799 var c = active.clone();
1809 if(active.length < 1){
1810 Roo.get(document).un("mouseup", onMouseDown);
1818 var last = active.last();
1819 lastShow = new Date();
1822 Roo.get(document).on("mouseup", onMouseDown);
1827 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1828 m.parentMenu.activeChild = m;
1829 }else if(last && last.isVisible()){
1830 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1835 function onBeforeHide(m){
1837 m.activeChild.hide();
1839 if(m.autoHideTimer){
1840 clearTimeout(m.autoHideTimer);
1841 delete m.autoHideTimer;
1846 function onBeforeShow(m){
1847 var pm = m.parentMenu;
1848 if(!pm && !m.allowOtherMenus){
1850 }else if(pm && pm.activeChild && active != m){
1851 pm.activeChild.hide();
1855 // private this should really trigger on mouseup..
1856 function onMouseDown(e){
1857 Roo.log("on Mouse Up");
1859 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1860 Roo.log("MenuManager hideAll");
1869 function onBeforeCheck(mi, state){
1871 var g = groups[mi.group];
1872 for(var i = 0, l = g.length; i < l; i++){
1874 g[i].setChecked(false);
1883 * Hides all menus that are currently visible
1885 hideAll : function(){
1890 register : function(menu){
1894 menus[menu.id] = menu;
1895 menu.on("beforehide", onBeforeHide);
1896 menu.on("hide", onHide);
1897 menu.on("beforeshow", onBeforeShow);
1898 menu.on("show", onShow);
1900 if(g && menu.events["checkchange"]){
1904 groups[g].push(menu);
1905 menu.on("checkchange", onCheck);
1910 * Returns a {@link Roo.menu.Menu} object
1911 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1912 * be used to generate and return a new Menu instance.
1914 get : function(menu){
1915 if(typeof menu == "string"){ // menu id
1917 }else if(menu.events){ // menu instance
1920 /*else if(typeof menu.length == 'number'){ // array of menu items?
1921 return new Roo.bootstrap.Menu({items:menu});
1922 }else{ // otherwise, must be a config
1923 return new Roo.bootstrap.Menu(menu);
1930 unregister : function(menu){
1931 delete menus[menu.id];
1932 menu.un("beforehide", onBeforeHide);
1933 menu.un("hide", onHide);
1934 menu.un("beforeshow", onBeforeShow);
1935 menu.un("show", onShow);
1937 if(g && menu.events["checkchange"]){
1938 groups[g].remove(menu);
1939 menu.un("checkchange", onCheck);
1944 registerCheckable : function(menuItem){
1945 var g = menuItem.group;
1950 groups[g].push(menuItem);
1951 menuItem.on("beforecheckchange", onBeforeCheck);
1956 unregisterCheckable : function(menuItem){
1957 var g = menuItem.group;
1959 groups[g].remove(menuItem);
1960 menuItem.un("beforecheckchange", onBeforeCheck);
1972 * @class Roo.bootstrap.Menu
1973 * @extends Roo.bootstrap.Component
1974 * Bootstrap Menu class - container for MenuItems
1975 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1976 * @cfg {bool} hidden if the menu should be hidden when rendered.
1977 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1978 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1982 * @param {Object} config The config object
1986 Roo.bootstrap.Menu = function(config){
1987 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1988 if (this.registerMenu && this.type != 'treeview') {
1989 Roo.bootstrap.MenuMgr.register(this);
1994 * Fires before this menu is displayed
1995 * @param {Roo.menu.Menu} this
2000 * Fires before this menu is hidden
2001 * @param {Roo.menu.Menu} this
2006 * Fires after this menu is displayed
2007 * @param {Roo.menu.Menu} this
2012 * Fires after this menu is hidden
2013 * @param {Roo.menu.Menu} this
2018 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2019 * @param {Roo.menu.Menu} this
2020 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2021 * @param {Roo.EventObject} e
2026 * Fires when the mouse is hovering over this menu
2027 * @param {Roo.menu.Menu} this
2028 * @param {Roo.EventObject} e
2029 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2034 * Fires when the mouse exits this menu
2035 * @param {Roo.menu.Menu} this
2036 * @param {Roo.EventObject} e
2037 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2042 * Fires when a menu item contained in this menu is clicked
2043 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2044 * @param {Roo.EventObject} e
2048 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2051 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2055 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2058 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2060 registerMenu : true,
2062 menuItems :false, // stores the menu items..
2072 getChildContainer : function() {
2076 getAutoCreate : function(){
2078 //if (['right'].indexOf(this.align)!==-1) {
2079 // cfg.cn[1].cls += ' pull-right'
2085 cls : 'dropdown-menu' ,
2086 style : 'z-index:1000'
2090 if (this.type === 'submenu') {
2091 cfg.cls = 'submenu active';
2093 if (this.type === 'treeview') {
2094 cfg.cls = 'treeview-menu';
2099 initEvents : function() {
2101 // Roo.log("ADD event");
2102 // Roo.log(this.triggerEl.dom);
2104 this.triggerEl.on('click', this.onTriggerClick, this);
2106 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2108 this.triggerEl.addClass('dropdown-toggle');
2111 this.el.on('touchstart' , this.onTouch, this);
2113 this.el.on('click' , this.onClick, this);
2115 this.el.on("mouseover", this.onMouseOver, this);
2116 this.el.on("mouseout", this.onMouseOut, this);
2120 findTargetItem : function(e)
2122 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2126 //Roo.log(t); Roo.log(t.id);
2128 //Roo.log(this.menuitems);
2129 return this.menuitems.get(t.id);
2131 //return this.items.get(t.menuItemId);
2137 onTouch : function(e)
2139 Roo.log("menu.onTouch");
2140 //e.stopEvent(); this make the user popdown broken
2144 onClick : function(e)
2146 Roo.log("menu.onClick");
2148 var t = this.findTargetItem(e);
2149 if(!t || t.isContainer){
2154 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2155 if(t == this.activeItem && t.shouldDeactivate(e)){
2156 this.activeItem.deactivate();
2157 delete this.activeItem;
2161 this.setActiveItem(t, true);
2169 Roo.log('pass click event');
2173 this.fireEvent("click", this, t, e);
2177 if(!t.href.length || t.href == '#'){
2178 (function() { _this.hide(); }).defer(100);
2183 onMouseOver : function(e){
2184 var t = this.findTargetItem(e);
2187 // if(t.canActivate && !t.disabled){
2188 // this.setActiveItem(t, true);
2192 this.fireEvent("mouseover", this, e, t);
2194 isVisible : function(){
2195 return !this.hidden;
2197 onMouseOut : function(e){
2198 var t = this.findTargetItem(e);
2201 // if(t == this.activeItem && t.shouldDeactivate(e)){
2202 // this.activeItem.deactivate();
2203 // delete this.activeItem;
2206 this.fireEvent("mouseout", this, e, t);
2211 * Displays this menu relative to another element
2212 * @param {String/HTMLElement/Roo.Element} element The element to align to
2213 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2214 * the element (defaults to this.defaultAlign)
2215 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2217 show : function(el, pos, parentMenu){
2218 this.parentMenu = parentMenu;
2222 this.fireEvent("beforeshow", this);
2223 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2226 * Displays this menu at a specific xy position
2227 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2228 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2230 showAt : function(xy, parentMenu, /* private: */_e){
2231 this.parentMenu = parentMenu;
2236 this.fireEvent("beforeshow", this);
2237 //xy = this.el.adjustForConstraints(xy);
2241 this.hideMenuItems();
2242 this.hidden = false;
2243 this.triggerEl.addClass('open');
2245 // reassign x when hitting right
2246 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2247 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2250 // reassign y when hitting bottom
2251 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2252 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2255 // but the list may align on trigger left or trigger top... should it be a properity?
2257 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2262 this.fireEvent("show", this);
2268 this.doFocus.defer(50, this);
2272 doFocus : function(){
2274 this.focusEl.focus();
2279 * Hides this menu and optionally all parent menus
2280 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2282 hide : function(deep)
2285 this.hideMenuItems();
2286 if(this.el && this.isVisible()){
2287 this.fireEvent("beforehide", this);
2288 if(this.activeItem){
2289 this.activeItem.deactivate();
2290 this.activeItem = null;
2292 this.triggerEl.removeClass('open');;
2294 this.fireEvent("hide", this);
2296 if(deep === true && this.parentMenu){
2297 this.parentMenu.hide(true);
2301 onTriggerClick : function(e)
2303 Roo.log('trigger click');
2305 var target = e.getTarget();
2307 Roo.log(target.nodeName.toLowerCase());
2309 if(target.nodeName.toLowerCase() === 'i'){
2315 onTriggerPress : function(e)
2317 Roo.log('trigger press');
2318 //Roo.log(e.getTarget());
2319 // Roo.log(this.triggerEl.dom);
2321 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2322 var pel = Roo.get(e.getTarget());
2323 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2324 Roo.log('is treeview or dropdown?');
2328 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2332 if (this.isVisible()) {
2337 this.show(this.triggerEl, false, false);
2340 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2347 hideMenuItems : function()
2349 Roo.log("hide Menu Items");
2353 //$(backdrop).remove()
2354 this.el.select('.open',true).each(function(aa) {
2356 aa.removeClass('open');
2357 //var parent = getParent($(this))
2358 //var relatedTarget = { relatedTarget: this }
2360 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2361 //if (e.isDefaultPrevented()) return
2362 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2365 addxtypeChild : function (tree, cntr) {
2366 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2368 this.menuitems.add(comp);
2380 this.getEl().dom.innerHTML = '';
2381 this.menuitems.clear();
2395 * @class Roo.bootstrap.MenuItem
2396 * @extends Roo.bootstrap.Component
2397 * Bootstrap MenuItem class
2398 * @cfg {String} html the menu label
2399 * @cfg {String} href the link
2400 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2401 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2402 * @cfg {Boolean} active used on sidebars to highlight active itesm
2403 * @cfg {String} fa favicon to show on left of menu item.
2404 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2408 * Create a new MenuItem
2409 * @param {Object} config The config object
2413 Roo.bootstrap.MenuItem = function(config){
2414 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2419 * The raw click event for the entire grid.
2420 * @param {Roo.bootstrap.MenuItem} this
2421 * @param {Roo.EventObject} e
2427 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2431 preventDefault: false,
2432 isContainer : false,
2436 getAutoCreate : function(){
2438 if(this.isContainer){
2441 cls: 'dropdown-menu-item'
2455 if (this.fa !== false) {
2458 cls : 'fa fa-' + this.fa
2467 cls: 'dropdown-menu-item',
2470 if (this.parent().type == 'treeview') {
2471 cfg.cls = 'treeview-menu';
2474 cfg.cls += ' active';
2479 anc.href = this.href || cfg.cn[0].href ;
2480 ctag.html = this.html || cfg.cn[0].html ;
2484 initEvents: function()
2486 if (this.parent().type == 'treeview') {
2487 this.el.select('a').on('click', this.onClick, this);
2491 this.menu.parentType = this.xtype;
2492 this.menu.triggerEl = this.el;
2493 this.menu = this.addxtype(Roo.apply({}, this.menu));
2497 onClick : function(e)
2499 Roo.log('item on click ');
2501 if(this.preventDefault){
2504 //this.parent().hideMenuItems();
2506 this.fireEvent('click', this, e);
2525 * @class Roo.bootstrap.MenuSeparator
2526 * @extends Roo.bootstrap.Component
2527 * Bootstrap MenuSeparator class
2530 * Create a new MenuItem
2531 * @param {Object} config The config object
2535 Roo.bootstrap.MenuSeparator = function(config){
2536 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2539 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2541 getAutoCreate : function(){
2560 * @class Roo.bootstrap.Modal
2561 * @extends Roo.bootstrap.Component
2562 * Bootstrap Modal class
2563 * @cfg {String} title Title of dialog
2564 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2565 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2566 * @cfg {Boolean} specificTitle default false
2567 * @cfg {Array} buttons Array of buttons or standard button set..
2568 * @cfg {String} buttonPosition (left|right|center) default right
2569 * @cfg {Boolean} animate default true
2570 * @cfg {Boolean} allow_close default true
2571 * @cfg {Boolean} fitwindow default false
2572 * @cfg {String} size (sm|lg) default empty
2573 * @cfg {Number} max_width set the max width of modal
2577 * Create a new Modal Dialog
2578 * @param {Object} config The config object
2581 Roo.bootstrap.Modal = function(config){
2582 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2587 * The raw btnclick event for the button
2588 * @param {Roo.EventObject} e
2593 * Fire when dialog resize
2594 * @param {Roo.bootstrap.Modal} this
2595 * @param {Roo.EventObject} e
2599 this.buttons = this.buttons || [];
2602 this.tmpl = Roo.factory(this.tmpl);
2607 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2609 title : 'test dialog',
2619 specificTitle: false,
2621 buttonPosition: 'right',
2644 onRender : function(ct, position)
2646 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2649 var cfg = Roo.apply({}, this.getAutoCreate());
2652 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2654 //if (!cfg.name.length) {
2658 cfg.cls += ' ' + this.cls;
2661 cfg.style = this.style;
2663 this.el = Roo.get(document.body).createChild(cfg, position);
2665 //var type = this.el.dom.type;
2668 if(this.tabIndex !== undefined){
2669 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2672 this.dialogEl = this.el.select('.modal-dialog',true).first();
2673 this.bodyEl = this.el.select('.modal-body',true).first();
2674 this.closeEl = this.el.select('.modal-header .close', true).first();
2675 this.headerEl = this.el.select('.modal-header',true).first();
2676 this.titleEl = this.el.select('.modal-title',true).first();
2677 this.footerEl = this.el.select('.modal-footer',true).first();
2679 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2681 //this.el.addClass("x-dlg-modal");
2683 if (this.buttons.length) {
2684 Roo.each(this.buttons, function(bb) {
2685 var b = Roo.apply({}, bb);
2686 b.xns = b.xns || Roo.bootstrap;
2687 b.xtype = b.xtype || 'Button';
2688 if (typeof(b.listeners) == 'undefined') {
2689 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2692 var btn = Roo.factory(b);
2694 btn.render(this.el.select('.modal-footer div').first());
2698 // render the children.
2701 if(typeof(this.items) != 'undefined'){
2702 var items = this.items;
2705 for(var i =0;i < items.length;i++) {
2706 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2710 this.items = nitems;
2712 // where are these used - they used to be body/close/footer
2716 //this.el.addClass([this.fieldClass, this.cls]);
2720 getAutoCreate : function()
2724 html : this.html || ''
2729 cls : 'modal-title',
2733 if(this.specificTitle){
2739 if (this.allow_close) {
2751 if(this.size.length){
2752 size = 'modal-' + this.size;
2759 cls: "modal-dialog " + size,
2762 cls : "modal-content",
2765 cls : 'modal-header',
2770 cls : 'modal-footer',
2774 cls: 'btn-' + this.buttonPosition
2791 modal.cls += ' fade';
2797 getChildContainer : function() {
2802 getButtonContainer : function() {
2803 return this.el.select('.modal-footer div',true).first();
2806 initEvents : function()
2808 if (this.allow_close) {
2809 this.closeEl.on('click', this.hide, this);
2811 Roo.EventManager.onWindowResize(this.resize, this, true);
2818 this.maskEl.setSize(
2819 Roo.lib.Dom.getViewWidth(true),
2820 Roo.lib.Dom.getViewHeight(true)
2823 if (this.fitwindow) {
2825 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2826 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2831 if(this.max_width !== 0) {
2833 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2836 this.setSize(w, this.height);
2840 if(this.max_height) {
2841 this.setSize(w,Math.min(
2843 Roo.lib.Dom.getViewportHeight(true) - 60
2849 if(!this.fit_content) {
2850 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2854 this.setSize(w, Math.min(
2856 this.headerEl.getHeight() +
2857 this.footerEl.getHeight() +
2858 this.getChildHeight(this.bodyEl.dom.childNodes),
2859 Roo.lib.Dom.getViewportHeight(true) - 60)
2865 setSize : function(w,h)
2876 if (!this.rendered) {
2880 //this.el.setStyle('display', 'block');
2881 this.el.removeClass('hideing');
2882 this.el.addClass('show');
2884 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2887 this.el.addClass('in');
2890 this.el.addClass('in');
2893 // not sure how we can show data in here..
2895 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2898 Roo.get(document.body).addClass("x-body-masked");
2900 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2901 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2902 this.maskEl.addClass('show');
2906 this.fireEvent('show', this);
2908 // set zindex here - otherwise it appears to be ignored...
2909 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2912 this.items.forEach( function(e) {
2913 e.layout ? e.layout() : false;
2921 if(this.fireEvent("beforehide", this) !== false){
2922 this.maskEl.removeClass('show');
2923 Roo.get(document.body).removeClass("x-body-masked");
2924 this.el.removeClass('in');
2925 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2927 if(this.animate){ // why
2928 this.el.addClass('hideing');
2930 if (!this.el.hasClass('hideing')) {
2931 return; // it's been shown again...
2933 this.el.removeClass('show');
2934 this.el.removeClass('hideing');
2938 this.el.removeClass('show');
2940 this.fireEvent('hide', this);
2943 isVisible : function()
2946 return this.el.hasClass('show') && !this.el.hasClass('hideing');
2950 addButton : function(str, cb)
2954 var b = Roo.apply({}, { html : str } );
2955 b.xns = b.xns || Roo.bootstrap;
2956 b.xtype = b.xtype || 'Button';
2957 if (typeof(b.listeners) == 'undefined') {
2958 b.listeners = { click : cb.createDelegate(this) };
2961 var btn = Roo.factory(b);
2963 btn.render(this.el.select('.modal-footer div').first());
2969 setDefaultButton : function(btn)
2971 //this.el.select('.modal-footer').()
2975 resizeTo: function(w,h)
2979 this.dialogEl.setWidth(w);
2980 if (this.diff === false) {
2981 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2984 this.bodyEl.setHeight(h - this.diff);
2986 this.fireEvent('resize', this);
2989 setContentSize : function(w, h)
2993 onButtonClick: function(btn,e)
2996 this.fireEvent('btnclick', btn.name, e);
2999 * Set the title of the Dialog
3000 * @param {String} str new Title
3002 setTitle: function(str) {
3003 this.titleEl.dom.innerHTML = str;
3006 * Set the body of the Dialog
3007 * @param {String} str new Title
3009 setBody: function(str) {
3010 this.bodyEl.dom.innerHTML = str;
3013 * Set the body of the Dialog using the template
3014 * @param {Obj} data - apply this data to the template and replace the body contents.
3016 applyBody: function(obj)
3019 Roo.log("Error - using apply Body without a template");
3022 this.tmpl.overwrite(this.bodyEl, obj);
3025 getChildHeight : function(child_nodes)
3029 child_nodes.length == 0
3034 var child_height = 0;
3036 for(var i = 0; i < child_nodes.length; i++) {
3039 * for modal with tabs...
3040 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3042 var layout_childs = child_nodes[i].childNodes;
3044 for(var j = 0; j < layout_childs.length; j++) {
3046 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3048 var layout_body_childs = layout_childs[j].childNodes;
3050 for(var k = 0; k < layout_body_childs.length; k++) {
3052 if(layout_body_childs[k].classList.contains('navbar')) {
3053 child_height += layout_body_childs[k].offsetHeight;
3057 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3059 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3061 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3063 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3064 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3079 child_height += child_nodes[i].offsetHeight;
3080 // Roo.log(child_nodes[i].offsetHeight);
3083 return child_height;
3089 Roo.apply(Roo.bootstrap.Modal, {
3091 * Button config that displays a single OK button
3100 * Button config that displays Yes and No buttons
3116 * Button config that displays OK and Cancel buttons
3131 * Button config that displays Yes, No and Cancel buttons
3155 * messagebox - can be used as a replace
3159 * @class Roo.MessageBox
3160 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3164 Roo.Msg.alert('Status', 'Changes saved successfully.');
3166 // Prompt for user data:
3167 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3169 // process text value...
3173 // Show a dialog using config options:
3175 title:'Save Changes?',
3176 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3177 buttons: Roo.Msg.YESNOCANCEL,
3184 Roo.bootstrap.MessageBox = function(){
3185 var dlg, opt, mask, waitTimer;
3186 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3187 var buttons, activeTextEl, bwidth;
3191 var handleButton = function(button){
3193 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3197 var handleHide = function(){
3199 dlg.el.removeClass(opt.cls);
3202 // Roo.TaskMgr.stop(waitTimer);
3203 // waitTimer = null;
3208 var updateButtons = function(b){
3211 buttons["ok"].hide();
3212 buttons["cancel"].hide();
3213 buttons["yes"].hide();
3214 buttons["no"].hide();
3215 //dlg.footer.dom.style.display = 'none';
3218 dlg.footerEl.dom.style.display = '';
3219 for(var k in buttons){
3220 if(typeof buttons[k] != "function"){
3223 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3224 width += buttons[k].el.getWidth()+15;
3234 var handleEsc = function(d, k, e){
3235 if(opt && opt.closable !== false){
3245 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3246 * @return {Roo.BasicDialog} The BasicDialog element
3248 getDialog : function(){
3250 dlg = new Roo.bootstrap.Modal( {
3253 //constraintoviewport:false,
3255 //collapsible : false,
3260 //buttonAlign:"center",
3261 closeClick : function(){
3262 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3265 handleButton("cancel");
3270 dlg.on("hide", handleHide);
3272 //dlg.addKeyListener(27, handleEsc);
3274 this.buttons = buttons;
3275 var bt = this.buttonText;
3276 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3277 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3278 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3279 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3281 bodyEl = dlg.bodyEl.createChild({
3283 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3284 '<textarea class="roo-mb-textarea"></textarea>' +
3285 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3287 msgEl = bodyEl.dom.firstChild;
3288 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3289 textboxEl.enableDisplayMode();
3290 textboxEl.addKeyListener([10,13], function(){
3291 if(dlg.isVisible() && opt && opt.buttons){
3294 }else if(opt.buttons.yes){
3295 handleButton("yes");
3299 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3300 textareaEl.enableDisplayMode();
3301 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3302 progressEl.enableDisplayMode();
3304 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3305 var pf = progressEl.dom.firstChild;
3307 pp = Roo.get(pf.firstChild);
3308 pp.setHeight(pf.offsetHeight);
3316 * Updates the message box body text
3317 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3318 * the XHTML-compliant non-breaking space character '&#160;')
3319 * @return {Roo.MessageBox} This message box
3321 updateText : function(text)
3323 if(!dlg.isVisible() && !opt.width){
3324 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3325 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3327 msgEl.innerHTML = text || ' ';
3329 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3330 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3332 Math.min(opt.width || cw , this.maxWidth),
3333 Math.max(opt.minWidth || this.minWidth, bwidth)
3336 activeTextEl.setWidth(w);
3338 if(dlg.isVisible()){
3339 dlg.fixedcenter = false;
3341 // to big, make it scroll. = But as usual stupid IE does not support
3344 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3345 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3346 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3348 bodyEl.dom.style.height = '';
3349 bodyEl.dom.style.overflowY = '';
3352 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3354 bodyEl.dom.style.overflowX = '';
3357 dlg.setContentSize(w, bodyEl.getHeight());
3358 if(dlg.isVisible()){
3359 dlg.fixedcenter = true;
3365 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3366 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3367 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3368 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3369 * @return {Roo.MessageBox} This message box
3371 updateProgress : function(value, text){
3373 this.updateText(text);
3376 if (pp) { // weird bug on my firefox - for some reason this is not defined
3377 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3378 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3384 * Returns true if the message box is currently displayed
3385 * @return {Boolean} True if the message box is visible, else false
3387 isVisible : function(){
3388 return dlg && dlg.isVisible();
3392 * Hides the message box if it is displayed
3395 if(this.isVisible()){
3401 * Displays a new message box, or reinitializes an existing message box, based on the config options
3402 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3403 * The following config object properties are supported:
3405 Property Type Description
3406 ---------- --------------- ------------------------------------------------------------------------------------
3407 animEl String/Element An id or Element from which the message box should animate as it opens and
3408 closes (defaults to undefined)
3409 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3410 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3411 closable Boolean False to hide the top-right close button (defaults to true). Note that
3412 progress and wait dialogs will ignore this property and always hide the
3413 close button as they can only be closed programmatically.
3414 cls String A custom CSS class to apply to the message box element
3415 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3416 displayed (defaults to 75)
3417 fn Function A callback function to execute after closing the dialog. The arguments to the
3418 function will be btn (the name of the button that was clicked, if applicable,
3419 e.g. "ok"), and text (the value of the active text field, if applicable).
3420 Progress and wait dialogs will ignore this option since they do not respond to
3421 user actions and can only be closed programmatically, so any required function
3422 should be called by the same code after it closes the dialog.
3423 icon String A CSS class that provides a background image to be used as an icon for
3424 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3425 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3426 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3427 modal Boolean False to allow user interaction with the page while the message box is
3428 displayed (defaults to true)
3429 msg String A string that will replace the existing message box body text (defaults
3430 to the XHTML-compliant non-breaking space character ' ')
3431 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3432 progress Boolean True to display a progress bar (defaults to false)
3433 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3434 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3435 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3436 title String The title text
3437 value String The string value to set into the active textbox element if displayed
3438 wait Boolean True to display a progress bar (defaults to false)
3439 width Number The width of the dialog in pixels
3446 msg: 'Please enter your address:',
3448 buttons: Roo.MessageBox.OKCANCEL,
3451 animEl: 'addAddressBtn'
3454 * @param {Object} config Configuration options
3455 * @return {Roo.MessageBox} This message box
3457 show : function(options)
3460 // this causes nightmares if you show one dialog after another
3461 // especially on callbacks..
3463 if(this.isVisible()){
3466 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3467 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3468 Roo.log("New Dialog Message:" + options.msg )
3469 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3470 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3473 var d = this.getDialog();
3475 d.setTitle(opt.title || " ");
3476 d.closeEl.setDisplayed(opt.closable !== false);
3477 activeTextEl = textboxEl;
3478 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3483 textareaEl.setHeight(typeof opt.multiline == "number" ?
3484 opt.multiline : this.defaultTextHeight);
3485 activeTextEl = textareaEl;
3494 progressEl.setDisplayed(opt.progress === true);
3495 this.updateProgress(0);
3496 activeTextEl.dom.value = opt.value || "";
3498 dlg.setDefaultButton(activeTextEl);
3500 var bs = opt.buttons;
3504 }else if(bs && bs.yes){
3505 db = buttons["yes"];
3507 dlg.setDefaultButton(db);
3509 bwidth = updateButtons(opt.buttons);
3510 this.updateText(opt.msg);
3512 d.el.addClass(opt.cls);
3514 d.proxyDrag = opt.proxyDrag === true;
3515 d.modal = opt.modal !== false;
3516 d.mask = opt.modal !== false ? mask : false;
3518 // force it to the end of the z-index stack so it gets a cursor in FF
3519 document.body.appendChild(dlg.el.dom);
3520 d.animateTarget = null;
3521 d.show(options.animEl);
3527 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3528 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3529 * and closing the message box when the process is complete.
3530 * @param {String} title The title bar text
3531 * @param {String} msg The message box body text
3532 * @return {Roo.MessageBox} This message box
3534 progress : function(title, msg){
3541 minWidth: this.minProgressWidth,
3548 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3549 * If a callback function is passed it will be called after the user clicks the button, and the
3550 * id of the button that was clicked will be passed as the only parameter to the callback
3551 * (could also be the top-right close button).
3552 * @param {String} title The title bar text
3553 * @param {String} msg The message box body text
3554 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3555 * @param {Object} scope (optional) The scope of the callback function
3556 * @return {Roo.MessageBox} This message box
3558 alert : function(title, msg, fn, scope)
3573 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3574 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3575 * You are responsible for closing the message box when the process is complete.
3576 * @param {String} msg The message box body text
3577 * @param {String} title (optional) The title bar text
3578 * @return {Roo.MessageBox} This message box
3580 wait : function(msg, title){
3591 waitTimer = Roo.TaskMgr.start({
3593 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3601 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3602 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3603 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3604 * @param {String} title The title bar text
3605 * @param {String} msg The message box body text
3606 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3607 * @param {Object} scope (optional) The scope of the callback function
3608 * @return {Roo.MessageBox} This message box
3610 confirm : function(title, msg, fn, scope){
3614 buttons: this.YESNO,
3623 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3624 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3625 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3626 * (could also be the top-right close button) and the text that was entered will be passed as the two
3627 * parameters to the callback.
3628 * @param {String} title The title bar text
3629 * @param {String} msg The message box body text
3630 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3631 * @param {Object} scope (optional) The scope of the callback function
3632 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3633 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3634 * @return {Roo.MessageBox} This message box
3636 prompt : function(title, msg, fn, scope, multiline){
3640 buttons: this.OKCANCEL,
3645 multiline: multiline,
3652 * Button config that displays a single OK button
3657 * Button config that displays Yes and No buttons
3660 YESNO : {yes:true, no:true},
3662 * Button config that displays OK and Cancel buttons
3665 OKCANCEL : {ok:true, cancel:true},
3667 * Button config that displays Yes, No and Cancel buttons
3670 YESNOCANCEL : {yes:true, no:true, cancel:true},
3673 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3676 defaultTextHeight : 75,
3678 * The maximum width in pixels of the message box (defaults to 600)
3683 * The minimum width in pixels of the message box (defaults to 100)
3688 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3689 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3692 minProgressWidth : 250,
3694 * An object containing the default button text strings that can be overriden for localized language support.
3695 * Supported properties are: ok, cancel, yes and no.
3696 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3709 * Shorthand for {@link Roo.MessageBox}
3711 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3712 Roo.Msg = Roo.Msg || Roo.MessageBox;
3721 * @class Roo.bootstrap.Navbar
3722 * @extends Roo.bootstrap.Component
3723 * Bootstrap Navbar class
3726 * Create a new Navbar
3727 * @param {Object} config The config object
3731 Roo.bootstrap.Navbar = function(config){
3732 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3736 * @event beforetoggle
3737 * Fire before toggle the menu
3738 * @param {Roo.EventObject} e
3740 "beforetoggle" : true
3744 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3753 getAutoCreate : function(){
3756 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3760 initEvents :function ()
3762 //Roo.log(this.el.select('.navbar-toggle',true));
3763 this.el.select('.navbar-toggle',true).on('click', function() {
3764 if(this.fireEvent('beforetoggle', this) !== false){
3765 this.el.select('.navbar-collapse',true).toggleClass('in');
3775 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3777 var size = this.el.getSize();
3778 this.maskEl.setSize(size.width, size.height);
3779 this.maskEl.enableDisplayMode("block");
3788 getChildContainer : function()
3790 if (this.el.select('.collapse').getCount()) {
3791 return this.el.select('.collapse',true).first();
3824 * @class Roo.bootstrap.NavSimplebar
3825 * @extends Roo.bootstrap.Navbar
3826 * Bootstrap Sidebar class
3828 * @cfg {Boolean} inverse is inverted color
3830 * @cfg {String} type (nav | pills | tabs)
3831 * @cfg {Boolean} arrangement stacked | justified
3832 * @cfg {String} align (left | right) alignment
3834 * @cfg {Boolean} main (true|false) main nav bar? default false
3835 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3837 * @cfg {String} tag (header|footer|nav|div) default is nav
3843 * Create a new Sidebar
3844 * @param {Object} config The config object
3848 Roo.bootstrap.NavSimplebar = function(config){
3849 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3852 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3868 getAutoCreate : function(){
3872 tag : this.tag || 'div',
3885 this.type = this.type || 'nav';
3886 if (['tabs','pills'].indexOf(this.type)!==-1) {
3887 cfg.cn[0].cls += ' nav-' + this.type
3891 if (this.type!=='nav') {
3892 Roo.log('nav type must be nav/tabs/pills')
3894 cfg.cn[0].cls += ' navbar-nav'
3900 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3901 cfg.cn[0].cls += ' nav-' + this.arrangement;
3905 if (this.align === 'right') {
3906 cfg.cn[0].cls += ' navbar-right';
3910 cfg.cls += ' navbar-inverse';
3937 * @class Roo.bootstrap.NavHeaderbar
3938 * @extends Roo.bootstrap.NavSimplebar
3939 * Bootstrap Sidebar class
3941 * @cfg {String} brand what is brand
3942 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3943 * @cfg {String} brand_href href of the brand
3944 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3945 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3946 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3947 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3950 * Create a new Sidebar
3951 * @param {Object} config The config object
3955 Roo.bootstrap.NavHeaderbar = function(config){
3956 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3960 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3967 desktopCenter : false,
3970 getAutoCreate : function(){
3973 tag: this.nav || 'nav',
3980 if (this.desktopCenter) {
3981 cn.push({cls : 'container', cn : []});
3988 cls: 'navbar-header',
3993 cls: 'navbar-toggle',
3994 'data-toggle': 'collapse',
3999 html: 'Toggle navigation'
4021 cls: 'collapse navbar-collapse',
4025 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
4027 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4028 cfg.cls += ' navbar-' + this.position;
4030 // tag can override this..
4032 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4035 if (this.brand !== '') {
4038 href: this.brand_href ? this.brand_href : '#',
4039 cls: 'navbar-brand',
4047 cfg.cls += ' main-nav';
4055 getHeaderChildContainer : function()
4057 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4058 return this.el.select('.navbar-header',true).first();
4061 return this.getChildContainer();
4065 initEvents : function()
4067 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4069 if (this.autohide) {
4074 Roo.get(document).on('scroll',function(e) {
4075 var ns = Roo.get(document).getScroll().top;
4076 var os = prevScroll;
4080 ft.removeClass('slideDown');
4081 ft.addClass('slideUp');
4084 ft.removeClass('slideUp');
4085 ft.addClass('slideDown');
4106 * @class Roo.bootstrap.NavSidebar
4107 * @extends Roo.bootstrap.Navbar
4108 * Bootstrap Sidebar class
4111 * Create a new Sidebar
4112 * @param {Object} config The config object
4116 Roo.bootstrap.NavSidebar = function(config){
4117 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4120 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4122 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4124 getAutoCreate : function(){
4129 cls: 'sidebar sidebar-nav'
4151 * @class Roo.bootstrap.NavGroup
4152 * @extends Roo.bootstrap.Component
4153 * Bootstrap NavGroup class
4154 * @cfg {String} align (left|right)
4155 * @cfg {Boolean} inverse
4156 * @cfg {String} type (nav|pills|tab) default nav
4157 * @cfg {String} navId - reference Id for navbar.
4161 * Create a new nav group
4162 * @param {Object} config The config object
4165 Roo.bootstrap.NavGroup = function(config){
4166 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4169 Roo.bootstrap.NavGroup.register(this);
4173 * Fires when the active item changes
4174 * @param {Roo.bootstrap.NavGroup} this
4175 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4176 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4183 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4194 getAutoCreate : function()
4196 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4203 if (['tabs','pills'].indexOf(this.type)!==-1) {
4204 cfg.cls += ' nav-' + this.type
4206 if (this.type!=='nav') {
4207 Roo.log('nav type must be nav/tabs/pills')
4209 cfg.cls += ' navbar-nav'
4212 if (this.parent() && this.parent().sidebar) {
4215 cls: 'dashboard-menu sidebar-menu'
4221 if (this.form === true) {
4227 if (this.align === 'right') {
4228 cfg.cls += ' navbar-right';
4230 cfg.cls += ' navbar-left';
4234 if (this.align === 'right') {
4235 cfg.cls += ' navbar-right';
4239 cfg.cls += ' navbar-inverse';
4247 * sets the active Navigation item
4248 * @param {Roo.bootstrap.NavItem} the new current navitem
4250 setActiveItem : function(item)
4253 Roo.each(this.navItems, function(v){
4258 v.setActive(false, true);
4265 item.setActive(true, true);
4266 this.fireEvent('changed', this, item, prev);
4271 * gets the active Navigation item
4272 * @return {Roo.bootstrap.NavItem} the current navitem
4274 getActive : function()
4278 Roo.each(this.navItems, function(v){
4289 indexOfNav : function()
4293 Roo.each(this.navItems, function(v,i){
4304 * adds a Navigation item
4305 * @param {Roo.bootstrap.NavItem} the navitem to add
4307 addItem : function(cfg)
4309 var cn = new Roo.bootstrap.NavItem(cfg);
4311 cn.parentId = this.id;
4312 cn.onRender(this.el, null);
4316 * register a Navigation item
4317 * @param {Roo.bootstrap.NavItem} the navitem to add
4319 register : function(item)
4321 this.navItems.push( item);
4322 item.navId = this.navId;
4327 * clear all the Navigation item
4330 clearAll : function()
4333 this.el.dom.innerHTML = '';
4336 getNavItem: function(tabId)
4339 Roo.each(this.navItems, function(e) {
4340 if (e.tabId == tabId) {
4350 setActiveNext : function()
4352 var i = this.indexOfNav(this.getActive());
4353 if (i > this.navItems.length) {
4356 this.setActiveItem(this.navItems[i+1]);
4358 setActivePrev : function()
4360 var i = this.indexOfNav(this.getActive());
4364 this.setActiveItem(this.navItems[i-1]);
4366 clearWasActive : function(except) {
4367 Roo.each(this.navItems, function(e) {
4368 if (e.tabId != except.tabId && e.was_active) {
4369 e.was_active = false;
4376 getWasActive : function ()
4379 Roo.each(this.navItems, function(e) {
4394 Roo.apply(Roo.bootstrap.NavGroup, {
4398 * register a Navigation Group
4399 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4401 register : function(navgrp)
4403 this.groups[navgrp.navId] = navgrp;
4407 * fetch a Navigation Group based on the navigation ID
4408 * @param {string} the navgroup to add
4409 * @returns {Roo.bootstrap.NavGroup} the navgroup
4411 get: function(navId) {
4412 if (typeof(this.groups[navId]) == 'undefined') {
4414 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4416 return this.groups[navId] ;
4431 * @class Roo.bootstrap.NavItem
4432 * @extends Roo.bootstrap.Component
4433 * Bootstrap Navbar.NavItem class
4434 * @cfg {String} href link to
4435 * @cfg {String} html content of button
4436 * @cfg {String} badge text inside badge
4437 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4438 * @cfg {String} glyphicon name of glyphicon
4439 * @cfg {String} icon name of font awesome icon
4440 * @cfg {Boolean} active Is item active
4441 * @cfg {Boolean} disabled Is item disabled
4443 * @cfg {Boolean} preventDefault (true | false) default false
4444 * @cfg {String} tabId the tab that this item activates.
4445 * @cfg {String} tagtype (a|span) render as a href or span?
4446 * @cfg {Boolean} animateRef (true|false) link to element default false
4449 * Create a new Navbar Item
4450 * @param {Object} config The config object
4452 Roo.bootstrap.NavItem = function(config){
4453 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4458 * The raw click event for the entire grid.
4459 * @param {Roo.EventObject} e
4464 * Fires when the active item active state changes
4465 * @param {Roo.bootstrap.NavItem} this
4466 * @param {boolean} state the new state
4472 * Fires when scroll to element
4473 * @param {Roo.bootstrap.NavItem} this
4474 * @param {Object} options
4475 * @param {Roo.EventObject} e
4483 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4491 preventDefault : false,
4498 getAutoCreate : function(){
4507 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4509 if (this.disabled) {
4510 cfg.cls += ' disabled';
4513 if (this.href || this.html || this.glyphicon || this.icon) {
4517 href : this.href || "#",
4518 html: this.html || ''
4523 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4526 if(this.glyphicon) {
4527 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4532 cfg.cn[0].html += " <span class='caret'></span>";
4536 if (this.badge !== '') {
4538 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4546 initEvents: function()
4548 if (typeof (this.menu) != 'undefined') {
4549 this.menu.parentType = this.xtype;
4550 this.menu.triggerEl = this.el;
4551 this.menu = this.addxtype(Roo.apply({}, this.menu));
4554 this.el.select('a',true).on('click', this.onClick, this);
4556 if(this.tagtype == 'span'){
4557 this.el.select('span',true).on('click', this.onClick, this);
4560 // at this point parent should be available..
4561 this.parent().register(this);
4564 onClick : function(e)
4566 if (e.getTarget('.dropdown-menu-item')) {
4567 // did you click on a menu itemm.... - then don't trigger onclick..
4572 this.preventDefault ||
4575 Roo.log("NavItem - prevent Default?");
4579 if (this.disabled) {
4583 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4584 if (tg && tg.transition) {
4585 Roo.log("waiting for the transitionend");
4591 //Roo.log("fire event clicked");
4592 if(this.fireEvent('click', this, e) === false){
4596 if(this.tagtype == 'span'){
4600 //Roo.log(this.href);
4601 var ael = this.el.select('a',true).first();
4604 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4605 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4606 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4607 return; // ignore... - it's a 'hash' to another page.
4609 Roo.log("NavItem - prevent Default?");
4611 this.scrollToElement(e);
4615 var p = this.parent();
4617 if (['tabs','pills'].indexOf(p.type)!==-1) {
4618 if (typeof(p.setActiveItem) !== 'undefined') {
4619 p.setActiveItem(this);
4623 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4624 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4625 // remove the collapsed menu expand...
4626 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4630 isActive: function () {
4633 setActive : function(state, fire, is_was_active)
4635 if (this.active && !state && this.navId) {
4636 this.was_active = true;
4637 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4639 nv.clearWasActive(this);
4643 this.active = state;
4646 this.el.removeClass('active');
4647 } else if (!this.el.hasClass('active')) {
4648 this.el.addClass('active');
4651 this.fireEvent('changed', this, state);
4654 // show a panel if it's registered and related..
4656 if (!this.navId || !this.tabId || !state || is_was_active) {
4660 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4664 var pan = tg.getPanelByName(this.tabId);
4668 // if we can not flip to new panel - go back to old nav highlight..
4669 if (false == tg.showPanel(pan)) {
4670 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4672 var onav = nv.getWasActive();
4674 onav.setActive(true, false, true);
4683 // this should not be here...
4684 setDisabled : function(state)
4686 this.disabled = state;
4688 this.el.removeClass('disabled');
4689 } else if (!this.el.hasClass('disabled')) {
4690 this.el.addClass('disabled');
4696 * Fetch the element to display the tooltip on.
4697 * @return {Roo.Element} defaults to this.el
4699 tooltipEl : function()
4701 return this.el.select('' + this.tagtype + '', true).first();
4704 scrollToElement : function(e)
4706 var c = document.body;
4709 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4711 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4712 c = document.documentElement;
4715 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4721 var o = target.calcOffsetsTo(c);
4728 this.fireEvent('scrollto', this, options, e);
4730 Roo.get(c).scrollTo('top', options.value, true);
4743 * <span> icon </span>
4744 * <span> text </span>
4745 * <span>badge </span>
4749 * @class Roo.bootstrap.NavSidebarItem
4750 * @extends Roo.bootstrap.NavItem
4751 * Bootstrap Navbar.NavSidebarItem class
4752 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4753 * {Boolean} open is the menu open
4754 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4755 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4756 * {String} buttonSize (sm|md|lg)the extra classes for the button
4757 * {Boolean} showArrow show arrow next to the text (default true)
4759 * Create a new Navbar Button
4760 * @param {Object} config The config object
4762 Roo.bootstrap.NavSidebarItem = function(config){
4763 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4768 * The raw click event for the entire grid.
4769 * @param {Roo.EventObject} e
4774 * Fires when the active item active state changes
4775 * @param {Roo.bootstrap.NavSidebarItem} this
4776 * @param {boolean} state the new state
4784 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4786 badgeWeight : 'default',
4792 buttonWeight : 'default',
4798 getAutoCreate : function(){
4803 href : this.href || '#',
4809 if(this.buttonView){
4812 href : this.href || '#',
4813 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4826 cfg.cls += ' active';
4829 if (this.disabled) {
4830 cfg.cls += ' disabled';
4833 cfg.cls += ' open x-open';
4836 if (this.glyphicon || this.icon) {
4837 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4838 a.cn.push({ tag : 'i', cls : c }) ;
4841 if(!this.buttonView){
4844 html : this.html || ''
4851 if (this.badge !== '') {
4852 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4858 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4861 a.cls += ' dropdown-toggle treeview' ;
4867 initEvents : function()
4869 if (typeof (this.menu) != 'undefined') {
4870 this.menu.parentType = this.xtype;
4871 this.menu.triggerEl = this.el;
4872 this.menu = this.addxtype(Roo.apply({}, this.menu));
4875 this.el.on('click', this.onClick, this);
4877 if(this.badge !== ''){
4878 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4883 onClick : function(e)
4890 if(this.preventDefault){
4894 this.fireEvent('click', this);
4897 disable : function()
4899 this.setDisabled(true);
4904 this.setDisabled(false);
4907 setDisabled : function(state)
4909 if(this.disabled == state){
4913 this.disabled = state;
4916 this.el.addClass('disabled');
4920 this.el.removeClass('disabled');
4925 setActive : function(state)
4927 if(this.active == state){
4931 this.active = state;
4934 this.el.addClass('active');
4938 this.el.removeClass('active');
4943 isActive: function ()
4948 setBadge : function(str)
4954 this.badgeEl.dom.innerHTML = str;
4971 * @class Roo.bootstrap.Row
4972 * @extends Roo.bootstrap.Component
4973 * Bootstrap Row class (contains columns...)
4977 * @param {Object} config The config object
4980 Roo.bootstrap.Row = function(config){
4981 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4984 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4986 getAutoCreate : function(){
5005 * @class Roo.bootstrap.Element
5006 * @extends Roo.bootstrap.Component
5007 * Bootstrap Element class
5008 * @cfg {String} html contents of the element
5009 * @cfg {String} tag tag of the element
5010 * @cfg {String} cls class of the element
5011 * @cfg {Boolean} preventDefault (true|false) default false
5012 * @cfg {Boolean} clickable (true|false) default false
5015 * Create a new Element
5016 * @param {Object} config The config object
5019 Roo.bootstrap.Element = function(config){
5020 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5026 * When a element is chick
5027 * @param {Roo.bootstrap.Element} this
5028 * @param {Roo.EventObject} e
5034 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5039 preventDefault: false,
5042 getAutoCreate : function(){
5046 // cls: this.cls, double assign in parent class Component.js :: onRender
5053 initEvents: function()
5055 Roo.bootstrap.Element.superclass.initEvents.call(this);
5058 this.el.on('click', this.onClick, this);
5063 onClick : function(e)
5065 if(this.preventDefault){
5069 this.fireEvent('click', this, e);
5072 getValue : function()
5074 return this.el.dom.innerHTML;
5077 setValue : function(value)
5079 this.el.dom.innerHTML = value;
5094 * @class Roo.bootstrap.Pagination
5095 * @extends Roo.bootstrap.Component
5096 * Bootstrap Pagination class
5097 * @cfg {String} size xs | sm | md | lg
5098 * @cfg {Boolean} inverse false | true
5101 * Create a new Pagination
5102 * @param {Object} config The config object
5105 Roo.bootstrap.Pagination = function(config){
5106 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5109 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5115 getAutoCreate : function(){
5121 cfg.cls += ' inverse';
5127 cfg.cls += " " + this.cls;
5145 * @class Roo.bootstrap.PaginationItem
5146 * @extends Roo.bootstrap.Component
5147 * Bootstrap PaginationItem class
5148 * @cfg {String} html text
5149 * @cfg {String} href the link
5150 * @cfg {Boolean} preventDefault (true | false) default true
5151 * @cfg {Boolean} active (true | false) default false
5152 * @cfg {Boolean} disabled default false
5156 * Create a new PaginationItem
5157 * @param {Object} config The config object
5161 Roo.bootstrap.PaginationItem = function(config){
5162 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5167 * The raw click event for the entire grid.
5168 * @param {Roo.EventObject} e
5174 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5178 preventDefault: true,
5183 getAutoCreate : function(){
5189 href : this.href ? this.href : '#',
5190 html : this.html ? this.html : ''
5200 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5204 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5210 initEvents: function() {
5212 this.el.on('click', this.onClick, this);
5215 onClick : function(e)
5217 Roo.log('PaginationItem on click ');
5218 if(this.preventDefault){
5226 this.fireEvent('click', this, e);
5242 * @class Roo.bootstrap.Slider
5243 * @extends Roo.bootstrap.Component
5244 * Bootstrap Slider class
5247 * Create a new Slider
5248 * @param {Object} config The config object
5251 Roo.bootstrap.Slider = function(config){
5252 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5255 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5257 getAutoCreate : function(){
5261 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5265 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5277 * Ext JS Library 1.1.1
5278 * Copyright(c) 2006-2007, Ext JS, LLC.
5280 * Originally Released Under LGPL - original licence link has changed is not relivant.
5283 * <script type="text/javascript">
5288 * @class Roo.grid.ColumnModel
5289 * @extends Roo.util.Observable
5290 * This is the default implementation of a ColumnModel used by the Grid. It defines
5291 * the columns in the grid.
5294 var colModel = new Roo.grid.ColumnModel([
5295 {header: "Ticker", width: 60, sortable: true, locked: true},
5296 {header: "Company Name", width: 150, sortable: true},
5297 {header: "Market Cap.", width: 100, sortable: true},
5298 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5299 {header: "Employees", width: 100, sortable: true, resizable: false}
5304 * The config options listed for this class are options which may appear in each
5305 * individual column definition.
5306 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5308 * @param {Object} config An Array of column config objects. See this class's
5309 * config objects for details.
5311 Roo.grid.ColumnModel = function(config){
5313 * The config passed into the constructor
5315 this.config = config;
5318 // if no id, create one
5319 // if the column does not have a dataIndex mapping,
5320 // map it to the order it is in the config
5321 for(var i = 0, len = config.length; i < len; i++){
5323 if(typeof c.dataIndex == "undefined"){
5326 if(typeof c.renderer == "string"){
5327 c.renderer = Roo.util.Format[c.renderer];
5329 if(typeof c.id == "undefined"){
5332 if(c.editor && c.editor.xtype){
5333 c.editor = Roo.factory(c.editor, Roo.grid);
5335 if(c.editor && c.editor.isFormField){
5336 c.editor = new Roo.grid.GridEditor(c.editor);
5338 this.lookup[c.id] = c;
5342 * The width of columns which have no width specified (defaults to 100)
5345 this.defaultWidth = 100;
5348 * Default sortable of columns which have no sortable specified (defaults to false)
5351 this.defaultSortable = false;
5355 * @event widthchange
5356 * Fires when the width of a column changes.
5357 * @param {ColumnModel} this
5358 * @param {Number} columnIndex The column index
5359 * @param {Number} newWidth The new width
5361 "widthchange": true,
5363 * @event headerchange
5364 * Fires when the text of a header changes.
5365 * @param {ColumnModel} this
5366 * @param {Number} columnIndex The column index
5367 * @param {Number} newText The new header text
5369 "headerchange": true,
5371 * @event hiddenchange
5372 * Fires when a column is hidden or "unhidden".
5373 * @param {ColumnModel} this
5374 * @param {Number} columnIndex The column index
5375 * @param {Boolean} hidden true if hidden, false otherwise
5377 "hiddenchange": true,
5379 * @event columnmoved
5380 * Fires when a column is moved.
5381 * @param {ColumnModel} this
5382 * @param {Number} oldIndex
5383 * @param {Number} newIndex
5385 "columnmoved" : true,
5387 * @event columlockchange
5388 * Fires when a column's locked state is changed
5389 * @param {ColumnModel} this
5390 * @param {Number} colIndex
5391 * @param {Boolean} locked true if locked
5393 "columnlockchange" : true
5395 Roo.grid.ColumnModel.superclass.constructor.call(this);
5397 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5399 * @cfg {String} header The header text to display in the Grid view.
5402 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5403 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5404 * specified, the column's index is used as an index into the Record's data Array.
5407 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5408 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5411 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5412 * Defaults to the value of the {@link #defaultSortable} property.
5413 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5416 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5419 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5422 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5425 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5428 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5429 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5430 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5431 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5434 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5437 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5440 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5443 * @cfg {String} cursor (Optional)
5446 * @cfg {String} tooltip (Optional)
5449 * @cfg {Number} xs (Optional)
5452 * @cfg {Number} sm (Optional)
5455 * @cfg {Number} md (Optional)
5458 * @cfg {Number} lg (Optional)
5461 * Returns the id of the column at the specified index.
5462 * @param {Number} index The column index
5463 * @return {String} the id
5465 getColumnId : function(index){
5466 return this.config[index].id;
5470 * Returns the column for a specified id.
5471 * @param {String} id The column id
5472 * @return {Object} the column
5474 getColumnById : function(id){
5475 return this.lookup[id];
5480 * Returns the column for a specified dataIndex.
5481 * @param {String} dataIndex The column dataIndex
5482 * @return {Object|Boolean} the column or false if not found
5484 getColumnByDataIndex: function(dataIndex){
5485 var index = this.findColumnIndex(dataIndex);
5486 return index > -1 ? this.config[index] : false;
5490 * Returns the index for a specified column id.
5491 * @param {String} id The column id
5492 * @return {Number} the index, or -1 if not found
5494 getIndexById : function(id){
5495 for(var i = 0, len = this.config.length; i < len; i++){
5496 if(this.config[i].id == id){
5504 * Returns the index for a specified column dataIndex.
5505 * @param {String} dataIndex The column dataIndex
5506 * @return {Number} the index, or -1 if not found
5509 findColumnIndex : function(dataIndex){
5510 for(var i = 0, len = this.config.length; i < len; i++){
5511 if(this.config[i].dataIndex == dataIndex){
5519 moveColumn : function(oldIndex, newIndex){
5520 var c = this.config[oldIndex];
5521 this.config.splice(oldIndex, 1);
5522 this.config.splice(newIndex, 0, c);
5523 this.dataMap = null;
5524 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5527 isLocked : function(colIndex){
5528 return this.config[colIndex].locked === true;
5531 setLocked : function(colIndex, value, suppressEvent){
5532 if(this.isLocked(colIndex) == value){
5535 this.config[colIndex].locked = value;
5537 this.fireEvent("columnlockchange", this, colIndex, value);
5541 getTotalLockedWidth : function(){
5543 for(var i = 0; i < this.config.length; i++){
5544 if(this.isLocked(i) && !this.isHidden(i)){
5545 this.totalWidth += this.getColumnWidth(i);
5551 getLockedCount : function(){
5552 for(var i = 0, len = this.config.length; i < len; i++){
5553 if(!this.isLocked(i)){
5558 return this.config.length;
5562 * Returns the number of columns.
5565 getColumnCount : function(visibleOnly){
5566 if(visibleOnly === true){
5568 for(var i = 0, len = this.config.length; i < len; i++){
5569 if(!this.isHidden(i)){
5575 return this.config.length;
5579 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5580 * @param {Function} fn
5581 * @param {Object} scope (optional)
5582 * @return {Array} result
5584 getColumnsBy : function(fn, scope){
5586 for(var i = 0, len = this.config.length; i < len; i++){
5587 var c = this.config[i];
5588 if(fn.call(scope||this, c, i) === true){
5596 * Returns true if the specified column is sortable.
5597 * @param {Number} col The column index
5600 isSortable : function(col){
5601 if(typeof this.config[col].sortable == "undefined"){
5602 return this.defaultSortable;
5604 return this.config[col].sortable;
5608 * Returns the rendering (formatting) function defined for the column.
5609 * @param {Number} col The column index.
5610 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5612 getRenderer : function(col){
5613 if(!this.config[col].renderer){
5614 return Roo.grid.ColumnModel.defaultRenderer;
5616 return this.config[col].renderer;
5620 * Sets the rendering (formatting) function for a column.
5621 * @param {Number} col The column index
5622 * @param {Function} fn The function to use to process the cell's raw data
5623 * to return HTML markup for the grid view. The render function is called with
5624 * the following parameters:<ul>
5625 * <li>Data value.</li>
5626 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5627 * <li>css A CSS style string to apply to the table cell.</li>
5628 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5629 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5630 * <li>Row index</li>
5631 * <li>Column index</li>
5632 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5634 setRenderer : function(col, fn){
5635 this.config[col].renderer = fn;
5639 * Returns the width for the specified column.
5640 * @param {Number} col The column index
5643 getColumnWidth : function(col){
5644 return this.config[col].width * 1 || this.defaultWidth;
5648 * Sets the width for a column.
5649 * @param {Number} col The column index
5650 * @param {Number} width The new width
5652 setColumnWidth : function(col, width, suppressEvent){
5653 this.config[col].width = width;
5654 this.totalWidth = null;
5656 this.fireEvent("widthchange", this, col, width);
5661 * Returns the total width of all columns.
5662 * @param {Boolean} includeHidden True to include hidden column widths
5665 getTotalWidth : function(includeHidden){
5666 if(!this.totalWidth){
5667 this.totalWidth = 0;
5668 for(var i = 0, len = this.config.length; i < len; i++){
5669 if(includeHidden || !this.isHidden(i)){
5670 this.totalWidth += this.getColumnWidth(i);
5674 return this.totalWidth;
5678 * Returns the header for the specified column.
5679 * @param {Number} col The column index
5682 getColumnHeader : function(col){
5683 return this.config[col].header;
5687 * Sets the header for a column.
5688 * @param {Number} col The column index
5689 * @param {String} header The new header
5691 setColumnHeader : function(col, header){
5692 this.config[col].header = header;
5693 this.fireEvent("headerchange", this, col, header);
5697 * Returns the tooltip for the specified column.
5698 * @param {Number} col The column index
5701 getColumnTooltip : function(col){
5702 return this.config[col].tooltip;
5705 * Sets the tooltip for a column.
5706 * @param {Number} col The column index
5707 * @param {String} tooltip The new tooltip
5709 setColumnTooltip : function(col, tooltip){
5710 this.config[col].tooltip = tooltip;
5714 * Returns the dataIndex for the specified column.
5715 * @param {Number} col The column index
5718 getDataIndex : function(col){
5719 return this.config[col].dataIndex;
5723 * Sets the dataIndex for a column.
5724 * @param {Number} col The column index
5725 * @param {Number} dataIndex The new dataIndex
5727 setDataIndex : function(col, dataIndex){
5728 this.config[col].dataIndex = dataIndex;
5734 * Returns true if the cell is editable.
5735 * @param {Number} colIndex The column index
5736 * @param {Number} rowIndex The row index - this is nto actually used..?
5739 isCellEditable : function(colIndex, rowIndex){
5740 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5744 * Returns the editor defined for the cell/column.
5745 * return false or null to disable editing.
5746 * @param {Number} colIndex The column index
5747 * @param {Number} rowIndex The row index
5750 getCellEditor : function(colIndex, rowIndex){
5751 return this.config[colIndex].editor;
5755 * Sets if a column is editable.
5756 * @param {Number} col The column index
5757 * @param {Boolean} editable True if the column is editable
5759 setEditable : function(col, editable){
5760 this.config[col].editable = editable;
5765 * Returns true if the column is hidden.
5766 * @param {Number} colIndex The column index
5769 isHidden : function(colIndex){
5770 return this.config[colIndex].hidden;
5775 * Returns true if the column width cannot be changed
5777 isFixed : function(colIndex){
5778 return this.config[colIndex].fixed;
5782 * Returns true if the column can be resized
5785 isResizable : function(colIndex){
5786 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5789 * Sets if a column is hidden.
5790 * @param {Number} colIndex The column index
5791 * @param {Boolean} hidden True if the column is hidden
5793 setHidden : function(colIndex, hidden){
5794 this.config[colIndex].hidden = hidden;
5795 this.totalWidth = null;
5796 this.fireEvent("hiddenchange", this, colIndex, hidden);
5800 * Sets the editor for a column.
5801 * @param {Number} col The column index
5802 * @param {Object} editor The editor object
5804 setEditor : function(col, editor){
5805 this.config[col].editor = editor;
5809 Roo.grid.ColumnModel.defaultRenderer = function(value)
5811 if(typeof value == "object") {
5814 if(typeof value == "string" && value.length < 1){
5818 return String.format("{0}", value);
5821 // Alias for backwards compatibility
5822 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5825 * Ext JS Library 1.1.1
5826 * Copyright(c) 2006-2007, Ext JS, LLC.
5828 * Originally Released Under LGPL - original licence link has changed is not relivant.
5831 * <script type="text/javascript">
5835 * @class Roo.LoadMask
5836 * A simple utility class for generically masking elements while loading data. If the element being masked has
5837 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5838 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5839 * element's UpdateManager load indicator and will be destroyed after the initial load.
5841 * Create a new LoadMask
5842 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5843 * @param {Object} config The config object
5845 Roo.LoadMask = function(el, config){
5846 this.el = Roo.get(el);
5847 Roo.apply(this, config);
5849 this.store.on('beforeload', this.onBeforeLoad, this);
5850 this.store.on('load', this.onLoad, this);
5851 this.store.on('loadexception', this.onLoadException, this);
5852 this.removeMask = false;
5854 var um = this.el.getUpdateManager();
5855 um.showLoadIndicator = false; // disable the default indicator
5856 um.on('beforeupdate', this.onBeforeLoad, this);
5857 um.on('update', this.onLoad, this);
5858 um.on('failure', this.onLoad, this);
5859 this.removeMask = true;
5863 Roo.LoadMask.prototype = {
5865 * @cfg {Boolean} removeMask
5866 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5867 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5871 * The text to display in a centered loading message box (defaults to 'Loading...')
5875 * @cfg {String} msgCls
5876 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5878 msgCls : 'x-mask-loading',
5881 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5887 * Disables the mask to prevent it from being displayed
5889 disable : function(){
5890 this.disabled = true;
5894 * Enables the mask so that it can be displayed
5896 enable : function(){
5897 this.disabled = false;
5900 onLoadException : function()
5904 if (typeof(arguments[3]) != 'undefined') {
5905 Roo.MessageBox.alert("Error loading",arguments[3]);
5909 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5910 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5917 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5922 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5926 onBeforeLoad : function(){
5928 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5933 destroy : function(){
5935 this.store.un('beforeload', this.onBeforeLoad, this);
5936 this.store.un('load', this.onLoad, this);
5937 this.store.un('loadexception', this.onLoadException, this);
5939 var um = this.el.getUpdateManager();
5940 um.un('beforeupdate', this.onBeforeLoad, this);
5941 um.un('update', this.onLoad, this);
5942 um.un('failure', this.onLoad, this);
5953 * @class Roo.bootstrap.Table
5954 * @extends Roo.bootstrap.Component
5955 * Bootstrap Table class
5956 * @cfg {String} cls table class
5957 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5958 * @cfg {String} bgcolor Specifies the background color for a table
5959 * @cfg {Number} border Specifies whether the table cells should have borders or not
5960 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5961 * @cfg {Number} cellspacing Specifies the space between cells
5962 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5963 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5964 * @cfg {String} sortable Specifies that the table should be sortable
5965 * @cfg {String} summary Specifies a summary of the content of a table
5966 * @cfg {Number} width Specifies the width of a table
5967 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5969 * @cfg {boolean} striped Should the rows be alternative striped
5970 * @cfg {boolean} bordered Add borders to the table
5971 * @cfg {boolean} hover Add hover highlighting
5972 * @cfg {boolean} condensed Format condensed
5973 * @cfg {boolean} responsive Format condensed
5974 * @cfg {Boolean} loadMask (true|false) default false
5975 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5976 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5977 * @cfg {Boolean} rowSelection (true|false) default false
5978 * @cfg {Boolean} cellSelection (true|false) default false
5979 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5980 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5981 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5982 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
5986 * Create a new Table
5987 * @param {Object} config The config object
5990 Roo.bootstrap.Table = function(config){
5991 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5996 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5997 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5998 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5999 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6001 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6003 this.sm.grid = this;
6004 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6005 this.sm = this.selModel;
6006 this.sm.xmodule = this.xmodule || false;
6009 if (this.cm && typeof(this.cm.config) == 'undefined') {
6010 this.colModel = new Roo.grid.ColumnModel(this.cm);
6011 this.cm = this.colModel;
6012 this.cm.xmodule = this.xmodule || false;
6015 this.store= Roo.factory(this.store, Roo.data);
6016 this.ds = this.store;
6017 this.ds.xmodule = this.xmodule || false;
6020 if (this.footer && this.store) {
6021 this.footer.dataSource = this.ds;
6022 this.footer = Roo.factory(this.footer);
6029 * Fires when a cell is clicked
6030 * @param {Roo.bootstrap.Table} this
6031 * @param {Roo.Element} el
6032 * @param {Number} rowIndex
6033 * @param {Number} columnIndex
6034 * @param {Roo.EventObject} e
6038 * @event celldblclick
6039 * Fires when a cell is double clicked
6040 * @param {Roo.bootstrap.Table} this
6041 * @param {Roo.Element} el
6042 * @param {Number} rowIndex
6043 * @param {Number} columnIndex
6044 * @param {Roo.EventObject} e
6046 "celldblclick" : true,
6049 * Fires when a row is clicked
6050 * @param {Roo.bootstrap.Table} this
6051 * @param {Roo.Element} el
6052 * @param {Number} rowIndex
6053 * @param {Roo.EventObject} e
6057 * @event rowdblclick
6058 * Fires when a row is double clicked
6059 * @param {Roo.bootstrap.Table} this
6060 * @param {Roo.Element} el
6061 * @param {Number} rowIndex
6062 * @param {Roo.EventObject} e
6064 "rowdblclick" : true,
6067 * Fires when a mouseover occur
6068 * @param {Roo.bootstrap.Table} this
6069 * @param {Roo.Element} el
6070 * @param {Number} rowIndex
6071 * @param {Number} columnIndex
6072 * @param {Roo.EventObject} e
6077 * Fires when a mouseout occur
6078 * @param {Roo.bootstrap.Table} this
6079 * @param {Roo.Element} el
6080 * @param {Number} rowIndex
6081 * @param {Number} columnIndex
6082 * @param {Roo.EventObject} e
6087 * Fires when a row is rendered, so you can change add a style to it.
6088 * @param {Roo.bootstrap.Table} this
6089 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6093 * @event rowsrendered
6094 * Fires when all the rows have been rendered
6095 * @param {Roo.bootstrap.Table} this
6097 'rowsrendered' : true,
6099 * @event contextmenu
6100 * The raw contextmenu event for the entire grid.
6101 * @param {Roo.EventObject} e
6103 "contextmenu" : true,
6105 * @event rowcontextmenu
6106 * Fires when a row is right clicked
6107 * @param {Roo.bootstrap.Table} this
6108 * @param {Number} rowIndex
6109 * @param {Roo.EventObject} e
6111 "rowcontextmenu" : true,
6113 * @event cellcontextmenu
6114 * Fires when a cell is right clicked
6115 * @param {Roo.bootstrap.Table} this
6116 * @param {Number} rowIndex
6117 * @param {Number} cellIndex
6118 * @param {Roo.EventObject} e
6120 "cellcontextmenu" : true,
6122 * @event headercontextmenu
6123 * Fires when a header is right clicked
6124 * @param {Roo.bootstrap.Table} this
6125 * @param {Number} columnIndex
6126 * @param {Roo.EventObject} e
6128 "headercontextmenu" : true
6132 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6158 rowSelection : false,
6159 cellSelection : false,
6162 // Roo.Element - the tbody
6164 // Roo.Element - thead element
6167 container: false, // used by gridpanel...
6173 auto_hide_footer : false,
6175 getAutoCreate : function()
6177 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6184 if (this.scrollBody) {
6185 cfg.cls += ' table-body-fixed';
6188 cfg.cls += ' table-striped';
6192 cfg.cls += ' table-hover';
6194 if (this.bordered) {
6195 cfg.cls += ' table-bordered';
6197 if (this.condensed) {
6198 cfg.cls += ' table-condensed';
6200 if (this.responsive) {
6201 cfg.cls += ' table-responsive';
6205 cfg.cls+= ' ' +this.cls;
6208 // this lot should be simplifed...
6221 ].forEach(function(k) {
6229 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6232 if(this.store || this.cm){
6233 if(this.headerShow){
6234 cfg.cn.push(this.renderHeader());
6237 cfg.cn.push(this.renderBody());
6239 if(this.footerShow){
6240 cfg.cn.push(this.renderFooter());
6242 // where does this come from?
6243 //cfg.cls+= ' TableGrid';
6246 return { cn : [ cfg ] };
6249 initEvents : function()
6251 if(!this.store || !this.cm){
6254 if (this.selModel) {
6255 this.selModel.initEvents();
6259 //Roo.log('initEvents with ds!!!!');
6261 this.mainBody = this.el.select('tbody', true).first();
6262 this.mainHead = this.el.select('thead', true).first();
6263 this.mainFoot = this.el.select('tfoot', true).first();
6269 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6270 e.on('click', _this.sort, _this);
6273 this.mainBody.on("click", this.onClick, this);
6274 this.mainBody.on("dblclick", this.onDblClick, this);
6276 // why is this done????? = it breaks dialogs??
6277 //this.parent().el.setStyle('position', 'relative');
6281 this.footer.parentId = this.id;
6282 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6285 this.el.select('tfoot tr td').first().addClass('hide');
6290 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6293 this.store.on('load', this.onLoad, this);
6294 this.store.on('beforeload', this.onBeforeLoad, this);
6295 this.store.on('update', this.onUpdate, this);
6296 this.store.on('add', this.onAdd, this);
6297 this.store.on("clear", this.clear, this);
6299 this.el.on("contextmenu", this.onContextMenu, this);
6301 this.mainBody.on('scroll', this.onBodyScroll, this);
6303 this.cm.on("headerchange", this.onHeaderChange, this);
6305 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6309 onContextMenu : function(e, t)
6311 this.processEvent("contextmenu", e);
6314 processEvent : function(name, e)
6316 if (name != 'touchstart' ) {
6317 this.fireEvent(name, e);
6320 var t = e.getTarget();
6322 var cell = Roo.get(t);
6328 if(cell.findParent('tfoot', false, true)){
6332 if(cell.findParent('thead', false, true)){
6334 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6335 cell = Roo.get(t).findParent('th', false, true);
6337 Roo.log("failed to find th in thead?");
6338 Roo.log(e.getTarget());
6343 var cellIndex = cell.dom.cellIndex;
6345 var ename = name == 'touchstart' ? 'click' : name;
6346 this.fireEvent("header" + ename, this, cellIndex, e);
6351 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6352 cell = Roo.get(t).findParent('td', false, true);
6354 Roo.log("failed to find th in tbody?");
6355 Roo.log(e.getTarget());
6360 var row = cell.findParent('tr', false, true);
6361 var cellIndex = cell.dom.cellIndex;
6362 var rowIndex = row.dom.rowIndex - 1;
6366 this.fireEvent("row" + name, this, rowIndex, e);
6370 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6376 onMouseover : function(e, el)
6378 var cell = Roo.get(el);
6384 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6385 cell = cell.findParent('td', false, true);
6388 var row = cell.findParent('tr', false, true);
6389 var cellIndex = cell.dom.cellIndex;
6390 var rowIndex = row.dom.rowIndex - 1; // start from 0
6392 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6396 onMouseout : function(e, el)
6398 var cell = Roo.get(el);
6404 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6405 cell = cell.findParent('td', false, true);
6408 var row = cell.findParent('tr', false, true);
6409 var cellIndex = cell.dom.cellIndex;
6410 var rowIndex = row.dom.rowIndex - 1; // start from 0
6412 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6416 onClick : function(e, el)
6418 var cell = Roo.get(el);
6420 if(!cell || (!this.cellSelection && !this.rowSelection)){
6424 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6425 cell = cell.findParent('td', false, true);
6428 if(!cell || typeof(cell) == 'undefined'){
6432 var row = cell.findParent('tr', false, true);
6434 if(!row || typeof(row) == 'undefined'){
6438 var cellIndex = cell.dom.cellIndex;
6439 var rowIndex = this.getRowIndex(row);
6441 // why??? - should these not be based on SelectionModel?
6442 if(this.cellSelection){
6443 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6446 if(this.rowSelection){
6447 this.fireEvent('rowclick', this, row, rowIndex, e);
6453 onDblClick : function(e,el)
6455 var cell = Roo.get(el);
6457 if(!cell || (!this.cellSelection && !this.rowSelection)){
6461 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6462 cell = cell.findParent('td', false, true);
6465 if(!cell || typeof(cell) == 'undefined'){
6469 var row = cell.findParent('tr', false, true);
6471 if(!row || typeof(row) == 'undefined'){
6475 var cellIndex = cell.dom.cellIndex;
6476 var rowIndex = this.getRowIndex(row);
6478 if(this.cellSelection){
6479 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6482 if(this.rowSelection){
6483 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6487 sort : function(e,el)
6489 var col = Roo.get(el);
6491 if(!col.hasClass('sortable')){
6495 var sort = col.attr('sort');
6498 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6502 this.store.sortInfo = {field : sort, direction : dir};
6505 Roo.log("calling footer first");
6506 this.footer.onClick('first');
6509 this.store.load({ params : { start : 0 } });
6513 renderHeader : function()
6521 this.totalWidth = 0;
6523 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6525 var config = cm.config[i];
6529 cls : 'x-hcol-' + i,
6531 html: cm.getColumnHeader(i)
6536 if(typeof(config.sortable) != 'undefined' && config.sortable){
6538 c.html = '<i class="glyphicon"></i>' + c.html;
6541 if(typeof(config.lgHeader) != 'undefined'){
6542 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6545 if(typeof(config.mdHeader) != 'undefined'){
6546 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6549 if(typeof(config.smHeader) != 'undefined'){
6550 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6553 if(typeof(config.xsHeader) != 'undefined'){
6554 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6561 if(typeof(config.tooltip) != 'undefined'){
6562 c.tooltip = config.tooltip;
6565 if(typeof(config.colspan) != 'undefined'){
6566 c.colspan = config.colspan;
6569 if(typeof(config.hidden) != 'undefined' && config.hidden){
6570 c.style += ' display:none;';
6573 if(typeof(config.dataIndex) != 'undefined'){
6574 c.sort = config.dataIndex;
6579 if(typeof(config.align) != 'undefined' && config.align.length){
6580 c.style += ' text-align:' + config.align + ';';
6583 if(typeof(config.width) != 'undefined'){
6584 c.style += ' width:' + config.width + 'px;';
6585 this.totalWidth += config.width;
6587 this.totalWidth += 100; // assume minimum of 100 per column?
6590 if(typeof(config.cls) != 'undefined'){
6591 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6594 ['xs','sm','md','lg'].map(function(size){
6596 if(typeof(config[size]) == 'undefined'){
6600 if (!config[size]) { // 0 = hidden
6601 c.cls += ' hidden-' + size;
6605 c.cls += ' col-' + size + '-' + config[size];
6615 renderBody : function()
6625 colspan : this.cm.getColumnCount()
6635 renderFooter : function()
6645 colspan : this.cm.getColumnCount()
6659 // Roo.log('ds onload');
6664 var ds = this.store;
6666 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6667 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6668 if (_this.store.sortInfo) {
6670 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6671 e.select('i', true).addClass(['glyphicon-arrow-up']);
6674 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6675 e.select('i', true).addClass(['glyphicon-arrow-down']);
6680 var tbody = this.mainBody;
6682 if(ds.getCount() > 0){
6683 ds.data.each(function(d,rowIndex){
6684 var row = this.renderRow(cm, ds, rowIndex);
6686 tbody.createChild(row);
6690 if(row.cellObjects.length){
6691 Roo.each(row.cellObjects, function(r){
6692 _this.renderCellObject(r);
6699 var tfoot = this.el.select('tfoot', true).first();
6701 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6703 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6705 var total = this.ds.getTotalCount();
6707 if(this.footer.pageSize < total){
6708 this.mainFoot.show();
6712 Roo.each(this.el.select('tbody td', true).elements, function(e){
6713 e.on('mouseover', _this.onMouseover, _this);
6716 Roo.each(this.el.select('tbody td', true).elements, function(e){
6717 e.on('mouseout', _this.onMouseout, _this);
6719 this.fireEvent('rowsrendered', this);
6725 onUpdate : function(ds,record)
6727 this.refreshRow(record);
6731 onRemove : function(ds, record, index, isUpdate){
6732 if(isUpdate !== true){
6733 this.fireEvent("beforerowremoved", this, index, record);
6735 var bt = this.mainBody.dom;
6737 var rows = this.el.select('tbody > tr', true).elements;
6739 if(typeof(rows[index]) != 'undefined'){
6740 bt.removeChild(rows[index].dom);
6743 // if(bt.rows[index]){
6744 // bt.removeChild(bt.rows[index]);
6747 if(isUpdate !== true){
6748 //this.stripeRows(index);
6749 //this.syncRowHeights(index, index);
6751 this.fireEvent("rowremoved", this, index, record);
6755 onAdd : function(ds, records, rowIndex)
6757 //Roo.log('on Add called');
6758 // - note this does not handle multiple adding very well..
6759 var bt = this.mainBody.dom;
6760 for (var i =0 ; i < records.length;i++) {
6761 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6762 //Roo.log(records[i]);
6763 //Roo.log(this.store.getAt(rowIndex+i));
6764 this.insertRow(this.store, rowIndex + i, false);
6771 refreshRow : function(record){
6772 var ds = this.store, index;
6773 if(typeof record == 'number'){
6775 record = ds.getAt(index);
6777 index = ds.indexOf(record);
6779 this.insertRow(ds, index, true);
6781 this.onRemove(ds, record, index+1, true);
6783 //this.syncRowHeights(index, index);
6785 this.fireEvent("rowupdated", this, index, record);
6788 insertRow : function(dm, rowIndex, isUpdate){
6791 this.fireEvent("beforerowsinserted", this, rowIndex);
6793 //var s = this.getScrollState();
6794 var row = this.renderRow(this.cm, this.store, rowIndex);
6795 // insert before rowIndex..
6796 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6800 if(row.cellObjects.length){
6801 Roo.each(row.cellObjects, function(r){
6802 _this.renderCellObject(r);
6807 this.fireEvent("rowsinserted", this, rowIndex);
6808 //this.syncRowHeights(firstRow, lastRow);
6809 //this.stripeRows(firstRow);
6816 getRowDom : function(rowIndex)
6818 var rows = this.el.select('tbody > tr', true).elements;
6820 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6823 // returns the object tree for a tr..
6826 renderRow : function(cm, ds, rowIndex)
6828 var d = ds.getAt(rowIndex);
6832 cls : 'x-row-' + rowIndex,
6836 var cellObjects = [];
6838 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6839 var config = cm.config[i];
6841 var renderer = cm.getRenderer(i);
6845 if(typeof(renderer) !== 'undefined'){
6846 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6848 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6849 // and are rendered into the cells after the row is rendered - using the id for the element.
6851 if(typeof(value) === 'object'){
6861 rowIndex : rowIndex,
6866 this.fireEvent('rowclass', this, rowcfg);
6870 cls : rowcfg.rowClass + ' x-col-' + i,
6872 html: (typeof(value) === 'object') ? '' : value
6879 if(typeof(config.colspan) != 'undefined'){
6880 td.colspan = config.colspan;
6883 if(typeof(config.hidden) != 'undefined' && config.hidden){
6884 td.style += ' display:none;';
6887 if(typeof(config.align) != 'undefined' && config.align.length){
6888 td.style += ' text-align:' + config.align + ';';
6890 if(typeof(config.valign) != 'undefined' && config.valign.length){
6891 td.style += ' vertical-align:' + config.valign + ';';
6894 if(typeof(config.width) != 'undefined'){
6895 td.style += ' width:' + config.width + 'px;';
6898 if(typeof(config.cursor) != 'undefined'){
6899 td.style += ' cursor:' + config.cursor + ';';
6902 if(typeof(config.cls) != 'undefined'){
6903 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6906 ['xs','sm','md','lg'].map(function(size){
6908 if(typeof(config[size]) == 'undefined'){
6912 if (!config[size]) { // 0 = hidden
6913 td.cls += ' hidden-' + size;
6917 td.cls += ' col-' + size + '-' + config[size];
6925 row.cellObjects = cellObjects;
6933 onBeforeLoad : function()
6942 this.el.select('tbody', true).first().dom.innerHTML = '';
6945 * Show or hide a row.
6946 * @param {Number} rowIndex to show or hide
6947 * @param {Boolean} state hide
6949 setRowVisibility : function(rowIndex, state)
6951 var bt = this.mainBody.dom;
6953 var rows = this.el.select('tbody > tr', true).elements;
6955 if(typeof(rows[rowIndex]) == 'undefined'){
6958 rows[rowIndex].dom.style.display = state ? '' : 'none';
6962 getSelectionModel : function(){
6964 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6966 return this.selModel;
6969 * Render the Roo.bootstrap object from renderder
6971 renderCellObject : function(r)
6975 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6977 var t = r.cfg.render(r.container);
6980 Roo.each(r.cfg.cn, function(c){
6982 container: t.getChildContainer(),
6985 _this.renderCellObject(child);
6990 getRowIndex : function(row)
6994 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7005 * Returns the grid's underlying element = used by panel.Grid
7006 * @return {Element} The element
7008 getGridEl : function(){
7012 * Forces a resize - used by panel.Grid
7013 * @return {Element} The element
7015 autoSize : function()
7017 //var ctr = Roo.get(this.container.dom.parentElement);
7018 var ctr = Roo.get(this.el.dom);
7020 var thd = this.getGridEl().select('thead',true).first();
7021 var tbd = this.getGridEl().select('tbody', true).first();
7022 var tfd = this.getGridEl().select('tfoot', true).first();
7024 var cw = ctr.getWidth();
7028 tbd.setSize(ctr.getWidth(),
7029 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7031 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7034 cw = Math.max(cw, this.totalWidth);
7035 this.getGridEl().select('tr',true).setWidth(cw);
7036 // resize 'expandable coloumn?
7038 return; // we doe not have a view in this design..
7041 onBodyScroll: function()
7043 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7045 this.mainHead.setStyle({
7046 'position' : 'relative',
7047 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7053 var scrollHeight = this.mainBody.dom.scrollHeight;
7055 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7057 var height = this.mainBody.getHeight();
7059 if(scrollHeight - height == scrollTop) {
7061 var total = this.ds.getTotalCount();
7063 if(this.footer.cursor + this.footer.pageSize < total){
7065 this.footer.ds.load({
7067 start : this.footer.cursor + this.footer.pageSize,
7068 limit : this.footer.pageSize
7078 onHeaderChange : function()
7080 var header = this.renderHeader();
7081 var table = this.el.select('table', true).first();
7083 this.mainHead.remove();
7084 this.mainHead = table.createChild(header, this.mainBody, false);
7087 onHiddenChange : function(colModel, colIndex, hidden)
7089 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7090 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7092 this.CSS.updateRule(thSelector, "display", "");
7093 this.CSS.updateRule(tdSelector, "display", "");
7096 this.CSS.updateRule(thSelector, "display", "none");
7097 this.CSS.updateRule(tdSelector, "display", "none");
7100 this.onHeaderChange();
7104 setColumnWidth: function(col_index, width)
7106 // width = "md-2 xs-2..."
7107 if(!this.colModel.config[col_index]) {
7111 var w = width.split(" ");
7113 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7115 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7117 h_row[0].classList.replace(
7118 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7119 "col-"+size_cls[0]+"-"+size_cls[1]
7122 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7124 for(var i = 0; i < rows.length; i++) {
7126 for(var j = 0; w.length; j++) {
7128 var size_cls = w[j].split("-");
7130 if(!Number.isInteger(size_cls[1] * 1)) {
7134 if(!this.colModel.config[col_index][size_cls[0]]) {
7138 rows[i].classList.replace(
7139 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7140 "col-"+size_cls[0]+"-"+size_cls[1]
7157 * @class Roo.bootstrap.TableCell
7158 * @extends Roo.bootstrap.Component
7159 * Bootstrap TableCell class
7160 * @cfg {String} html cell contain text
7161 * @cfg {String} cls cell class
7162 * @cfg {String} tag cell tag (td|th) default td
7163 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7164 * @cfg {String} align Aligns the content in a cell
7165 * @cfg {String} axis Categorizes cells
7166 * @cfg {String} bgcolor Specifies the background color of a cell
7167 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7168 * @cfg {Number} colspan Specifies the number of columns a cell should span
7169 * @cfg {String} headers Specifies one or more header cells a cell is related to
7170 * @cfg {Number} height Sets the height of a cell
7171 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7172 * @cfg {Number} rowspan Sets the number of rows a cell should span
7173 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7174 * @cfg {String} valign Vertical aligns the content in a cell
7175 * @cfg {Number} width Specifies the width of a cell
7178 * Create a new TableCell
7179 * @param {Object} config The config object
7182 Roo.bootstrap.TableCell = function(config){
7183 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7186 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7206 getAutoCreate : function(){
7207 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7227 cfg.align=this.align
7233 cfg.bgcolor=this.bgcolor
7236 cfg.charoff=this.charoff
7239 cfg.colspan=this.colspan
7242 cfg.headers=this.headers
7245 cfg.height=this.height
7248 cfg.nowrap=this.nowrap
7251 cfg.rowspan=this.rowspan
7254 cfg.scope=this.scope
7257 cfg.valign=this.valign
7260 cfg.width=this.width
7279 * @class Roo.bootstrap.TableRow
7280 * @extends Roo.bootstrap.Component
7281 * Bootstrap TableRow class
7282 * @cfg {String} cls row class
7283 * @cfg {String} align Aligns the content in a table row
7284 * @cfg {String} bgcolor Specifies a background color for a table row
7285 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7286 * @cfg {String} valign Vertical aligns the content in a table row
7289 * Create a new TableRow
7290 * @param {Object} config The config object
7293 Roo.bootstrap.TableRow = function(config){
7294 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7297 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7305 getAutoCreate : function(){
7306 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7316 cfg.align = this.align;
7319 cfg.bgcolor = this.bgcolor;
7322 cfg.charoff = this.charoff;
7325 cfg.valign = this.valign;
7343 * @class Roo.bootstrap.TableBody
7344 * @extends Roo.bootstrap.Component
7345 * Bootstrap TableBody class
7346 * @cfg {String} cls element class
7347 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7348 * @cfg {String} align Aligns the content inside the element
7349 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7350 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7353 * Create a new TableBody
7354 * @param {Object} config The config object
7357 Roo.bootstrap.TableBody = function(config){
7358 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7361 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7369 getAutoCreate : function(){
7370 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7384 cfg.align = this.align;
7387 cfg.charoff = this.charoff;
7390 cfg.valign = this.valign;
7397 // initEvents : function()
7404 // this.store = Roo.factory(this.store, Roo.data);
7405 // this.store.on('load', this.onLoad, this);
7407 // this.store.load();
7411 // onLoad: function ()
7413 // this.fireEvent('load', this);
7423 * Ext JS Library 1.1.1
7424 * Copyright(c) 2006-2007, Ext JS, LLC.
7426 * Originally Released Under LGPL - original licence link has changed is not relivant.
7429 * <script type="text/javascript">
7432 // as we use this in bootstrap.
7433 Roo.namespace('Roo.form');
7435 * @class Roo.form.Action
7436 * Internal Class used to handle form actions
7438 * @param {Roo.form.BasicForm} el The form element or its id
7439 * @param {Object} config Configuration options
7444 // define the action interface
7445 Roo.form.Action = function(form, options){
7447 this.options = options || {};
7450 * Client Validation Failed
7453 Roo.form.Action.CLIENT_INVALID = 'client';
7455 * Server Validation Failed
7458 Roo.form.Action.SERVER_INVALID = 'server';
7460 * Connect to Server Failed
7463 Roo.form.Action.CONNECT_FAILURE = 'connect';
7465 * Reading Data from Server Failed
7468 Roo.form.Action.LOAD_FAILURE = 'load';
7470 Roo.form.Action.prototype = {
7472 failureType : undefined,
7473 response : undefined,
7477 run : function(options){
7482 success : function(response){
7487 handleResponse : function(response){
7491 // default connection failure
7492 failure : function(response){
7494 this.response = response;
7495 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7496 this.form.afterAction(this, false);
7499 processResponse : function(response){
7500 this.response = response;
7501 if(!response.responseText){
7504 this.result = this.handleResponse(response);
7508 // utility functions used internally
7509 getUrl : function(appendParams){
7510 var url = this.options.url || this.form.url || this.form.el.dom.action;
7512 var p = this.getParams();
7514 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7520 getMethod : function(){
7521 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7524 getParams : function(){
7525 var bp = this.form.baseParams;
7526 var p = this.options.params;
7528 if(typeof p == "object"){
7529 p = Roo.urlEncode(Roo.applyIf(p, bp));
7530 }else if(typeof p == 'string' && bp){
7531 p += '&' + Roo.urlEncode(bp);
7534 p = Roo.urlEncode(bp);
7539 createCallback : function(){
7541 success: this.success,
7542 failure: this.failure,
7544 timeout: (this.form.timeout*1000),
7545 upload: this.form.fileUpload ? this.success : undefined
7550 Roo.form.Action.Submit = function(form, options){
7551 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7554 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7557 haveProgress : false,
7558 uploadComplete : false,
7560 // uploadProgress indicator.
7561 uploadProgress : function()
7563 if (!this.form.progressUrl) {
7567 if (!this.haveProgress) {
7568 Roo.MessageBox.progress("Uploading", "Uploading");
7570 if (this.uploadComplete) {
7571 Roo.MessageBox.hide();
7575 this.haveProgress = true;
7577 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7579 var c = new Roo.data.Connection();
7581 url : this.form.progressUrl,
7586 success : function(req){
7587 //console.log(data);
7591 rdata = Roo.decode(req.responseText)
7593 Roo.log("Invalid data from server..");
7597 if (!rdata || !rdata.success) {
7599 Roo.MessageBox.alert(Roo.encode(rdata));
7602 var data = rdata.data;
7604 if (this.uploadComplete) {
7605 Roo.MessageBox.hide();
7610 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7611 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7614 this.uploadProgress.defer(2000,this);
7617 failure: function(data) {
7618 Roo.log('progress url failed ');
7629 // run get Values on the form, so it syncs any secondary forms.
7630 this.form.getValues();
7632 var o = this.options;
7633 var method = this.getMethod();
7634 var isPost = method == 'POST';
7635 if(o.clientValidation === false || this.form.isValid()){
7637 if (this.form.progressUrl) {
7638 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7639 (new Date() * 1) + '' + Math.random());
7644 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7645 form:this.form.el.dom,
7646 url:this.getUrl(!isPost),
7648 params:isPost ? this.getParams() : null,
7649 isUpload: this.form.fileUpload
7652 this.uploadProgress();
7654 }else if (o.clientValidation !== false){ // client validation failed
7655 this.failureType = Roo.form.Action.CLIENT_INVALID;
7656 this.form.afterAction(this, false);
7660 success : function(response)
7662 this.uploadComplete= true;
7663 if (this.haveProgress) {
7664 Roo.MessageBox.hide();
7668 var result = this.processResponse(response);
7669 if(result === true || result.success){
7670 this.form.afterAction(this, true);
7674 this.form.markInvalid(result.errors);
7675 this.failureType = Roo.form.Action.SERVER_INVALID;
7677 this.form.afterAction(this, false);
7679 failure : function(response)
7681 this.uploadComplete= true;
7682 if (this.haveProgress) {
7683 Roo.MessageBox.hide();
7686 this.response = response;
7687 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7688 this.form.afterAction(this, false);
7691 handleResponse : function(response){
7692 if(this.form.errorReader){
7693 var rs = this.form.errorReader.read(response);
7696 for(var i = 0, len = rs.records.length; i < len; i++) {
7697 var r = rs.records[i];
7701 if(errors.length < 1){
7705 success : rs.success,
7711 ret = Roo.decode(response.responseText);
7715 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7725 Roo.form.Action.Load = function(form, options){
7726 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7727 this.reader = this.form.reader;
7730 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7735 Roo.Ajax.request(Roo.apply(
7736 this.createCallback(), {
7737 method:this.getMethod(),
7738 url:this.getUrl(false),
7739 params:this.getParams()
7743 success : function(response){
7745 var result = this.processResponse(response);
7746 if(result === true || !result.success || !result.data){
7747 this.failureType = Roo.form.Action.LOAD_FAILURE;
7748 this.form.afterAction(this, false);
7751 this.form.clearInvalid();
7752 this.form.setValues(result.data);
7753 this.form.afterAction(this, true);
7756 handleResponse : function(response){
7757 if(this.form.reader){
7758 var rs = this.form.reader.read(response);
7759 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7761 success : rs.success,
7765 return Roo.decode(response.responseText);
7769 Roo.form.Action.ACTION_TYPES = {
7770 'load' : Roo.form.Action.Load,
7771 'submit' : Roo.form.Action.Submit
7780 * @class Roo.bootstrap.Form
7781 * @extends Roo.bootstrap.Component
7782 * Bootstrap Form class
7783 * @cfg {String} method GET | POST (default POST)
7784 * @cfg {String} labelAlign top | left (default top)
7785 * @cfg {String} align left | right - for navbars
7786 * @cfg {Boolean} loadMask load mask when submit (default true)
7791 * @param {Object} config The config object
7795 Roo.bootstrap.Form = function(config){
7797 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7799 Roo.bootstrap.Form.popover.apply();
7803 * @event clientvalidation
7804 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7805 * @param {Form} this
7806 * @param {Boolean} valid true if the form has passed client-side validation
7808 clientvalidation: true,
7810 * @event beforeaction
7811 * Fires before any action is performed. Return false to cancel the action.
7812 * @param {Form} this
7813 * @param {Action} action The action to be performed
7817 * @event actionfailed
7818 * Fires when an action fails.
7819 * @param {Form} this
7820 * @param {Action} action The action that failed
7822 actionfailed : true,
7824 * @event actioncomplete
7825 * Fires when an action is completed.
7826 * @param {Form} this
7827 * @param {Action} action The action that completed
7829 actioncomplete : true
7833 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7836 * @cfg {String} method
7837 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7842 * The URL to use for form actions if one isn't supplied in the action options.
7845 * @cfg {Boolean} fileUpload
7846 * Set to true if this form is a file upload.
7850 * @cfg {Object} baseParams
7851 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7855 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7859 * @cfg {Sting} align (left|right) for navbar forms
7864 activeAction : null,
7867 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7868 * element by passing it or its id or mask the form itself by passing in true.
7871 waitMsgTarget : false,
7876 * @cfg {Boolean} errorMask (true|false) default false
7881 * @cfg {Number} maskOffset Default 100
7886 * @cfg {Boolean} maskBody
7890 getAutoCreate : function(){
7894 method : this.method || 'POST',
7895 id : this.id || Roo.id(),
7898 if (this.parent().xtype.match(/^Nav/)) {
7899 cfg.cls = 'navbar-form navbar-' + this.align;
7903 if (this.labelAlign == 'left' ) {
7904 cfg.cls += ' form-horizontal';
7910 initEvents : function()
7912 this.el.on('submit', this.onSubmit, this);
7913 // this was added as random key presses on the form where triggering form submit.
7914 this.el.on('keypress', function(e) {
7915 if (e.getCharCode() != 13) {
7918 // we might need to allow it for textareas.. and some other items.
7919 // check e.getTarget().
7921 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7925 Roo.log("keypress blocked");
7933 onSubmit : function(e){
7938 * Returns true if client-side validation on the form is successful.
7941 isValid : function(){
7942 var items = this.getItems();
7946 items.each(function(f){
7952 Roo.log('invalid field: ' + f.name);
7956 if(!target && f.el.isVisible(true)){
7962 if(this.errorMask && !valid){
7963 Roo.bootstrap.Form.popover.mask(this, target);
7970 * Returns true if any fields in this form have changed since their original load.
7973 isDirty : function(){
7975 var items = this.getItems();
7976 items.each(function(f){
7986 * Performs a predefined action (submit or load) or custom actions you define on this form.
7987 * @param {String} actionName The name of the action type
7988 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7989 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7990 * accept other config options):
7992 Property Type Description
7993 ---------------- --------------- ----------------------------------------------------------------------------------
7994 url String The url for the action (defaults to the form's url)
7995 method String The form method to use (defaults to the form's method, or POST if not defined)
7996 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7997 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7998 validate the form on the client (defaults to false)
8000 * @return {BasicForm} this
8002 doAction : function(action, options){
8003 if(typeof action == 'string'){
8004 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8006 if(this.fireEvent('beforeaction', this, action) !== false){
8007 this.beforeAction(action);
8008 action.run.defer(100, action);
8014 beforeAction : function(action){
8015 var o = action.options;
8020 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8022 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8025 // not really supported yet.. ??
8027 //if(this.waitMsgTarget === true){
8028 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8029 //}else if(this.waitMsgTarget){
8030 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8031 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8033 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8039 afterAction : function(action, success){
8040 this.activeAction = null;
8041 var o = action.options;
8046 Roo.get(document.body).unmask();
8052 //if(this.waitMsgTarget === true){
8053 // this.el.unmask();
8054 //}else if(this.waitMsgTarget){
8055 // this.waitMsgTarget.unmask();
8057 // Roo.MessageBox.updateProgress(1);
8058 // Roo.MessageBox.hide();
8065 Roo.callback(o.success, o.scope, [this, action]);
8066 this.fireEvent('actioncomplete', this, action);
8070 // failure condition..
8071 // we have a scenario where updates need confirming.
8072 // eg. if a locking scenario exists..
8073 // we look for { errors : { needs_confirm : true }} in the response.
8075 (typeof(action.result) != 'undefined') &&
8076 (typeof(action.result.errors) != 'undefined') &&
8077 (typeof(action.result.errors.needs_confirm) != 'undefined')
8080 Roo.log("not supported yet");
8083 Roo.MessageBox.confirm(
8084 "Change requires confirmation",
8085 action.result.errorMsg,
8090 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8100 Roo.callback(o.failure, o.scope, [this, action]);
8101 // show an error message if no failed handler is set..
8102 if (!this.hasListener('actionfailed')) {
8103 Roo.log("need to add dialog support");
8105 Roo.MessageBox.alert("Error",
8106 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8107 action.result.errorMsg :
8108 "Saving Failed, please check your entries or try again"
8113 this.fireEvent('actionfailed', this, action);
8118 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8119 * @param {String} id The value to search for
8122 findField : function(id){
8123 var items = this.getItems();
8124 var field = items.get(id);
8126 items.each(function(f){
8127 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8134 return field || null;
8137 * Mark fields in this form invalid in bulk.
8138 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8139 * @return {BasicForm} this
8141 markInvalid : function(errors){
8142 if(errors instanceof Array){
8143 for(var i = 0, len = errors.length; i < len; i++){
8144 var fieldError = errors[i];
8145 var f = this.findField(fieldError.id);
8147 f.markInvalid(fieldError.msg);
8153 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8154 field.markInvalid(errors[id]);
8158 //Roo.each(this.childForms || [], function (f) {
8159 // f.markInvalid(errors);
8166 * Set values for fields in this form in bulk.
8167 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8168 * @return {BasicForm} this
8170 setValues : function(values){
8171 if(values instanceof Array){ // array of objects
8172 for(var i = 0, len = values.length; i < len; i++){
8174 var f = this.findField(v.id);
8176 f.setValue(v.value);
8177 if(this.trackResetOnLoad){
8178 f.originalValue = f.getValue();
8182 }else{ // object hash
8185 if(typeof values[id] != 'function' && (field = this.findField(id))){
8187 if (field.setFromData &&
8189 field.displayField &&
8190 // combos' with local stores can
8191 // be queried via setValue()
8192 // to set their value..
8193 (field.store && !field.store.isLocal)
8197 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8198 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8199 field.setFromData(sd);
8201 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8203 field.setFromData(values);
8206 field.setValue(values[id]);
8210 if(this.trackResetOnLoad){
8211 field.originalValue = field.getValue();
8217 //Roo.each(this.childForms || [], function (f) {
8218 // f.setValues(values);
8225 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8226 * they are returned as an array.
8227 * @param {Boolean} asString
8230 getValues : function(asString){
8231 //if (this.childForms) {
8232 // copy values from the child forms
8233 // Roo.each(this.childForms, function (f) {
8234 // this.setValues(f.getValues());
8240 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8241 if(asString === true){
8244 return Roo.urlDecode(fs);
8248 * Returns the fields in this form as an object with key/value pairs.
8249 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8252 getFieldValues : function(with_hidden)
8254 var items = this.getItems();
8256 items.each(function(f){
8262 var v = f.getValue();
8264 if (f.inputType =='radio') {
8265 if (typeof(ret[f.getName()]) == 'undefined') {
8266 ret[f.getName()] = ''; // empty..
8269 if (!f.el.dom.checked) {
8277 if(f.xtype == 'MoneyField'){
8278 ret[f.currencyName] = f.getCurrency();
8281 // not sure if this supported any more..
8282 if ((typeof(v) == 'object') && f.getRawValue) {
8283 v = f.getRawValue() ; // dates..
8285 // combo boxes where name != hiddenName...
8286 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8287 ret[f.name] = f.getRawValue();
8289 ret[f.getName()] = v;
8296 * Clears all invalid messages in this form.
8297 * @return {BasicForm} this
8299 clearInvalid : function(){
8300 var items = this.getItems();
8302 items.each(function(f){
8311 * @return {BasicForm} this
8314 var items = this.getItems();
8315 items.each(function(f){
8319 Roo.each(this.childForms || [], function (f) {
8327 getItems : function()
8329 var r=new Roo.util.MixedCollection(false, function(o){
8330 return o.id || (o.id = Roo.id());
8332 var iter = function(el) {
8339 Roo.each(el.items,function(e) {
8348 hideFields : function(items)
8350 Roo.each(items, function(i){
8352 var f = this.findField(i);
8363 showFields : function(items)
8365 Roo.each(items, function(i){
8367 var f = this.findField(i);
8380 Roo.apply(Roo.bootstrap.Form, {
8407 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8408 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8409 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8410 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8413 this.maskEl.top.enableDisplayMode("block");
8414 this.maskEl.left.enableDisplayMode("block");
8415 this.maskEl.bottom.enableDisplayMode("block");
8416 this.maskEl.right.enableDisplayMode("block");
8418 this.toolTip = new Roo.bootstrap.Tooltip({
8419 cls : 'roo-form-error-popover',
8421 'left' : ['r-l', [-2,0], 'right'],
8422 'right' : ['l-r', [2,0], 'left'],
8423 'bottom' : ['tl-bl', [0,2], 'top'],
8424 'top' : [ 'bl-tl', [0,-2], 'bottom']
8428 this.toolTip.render(Roo.get(document.body));
8430 this.toolTip.el.enableDisplayMode("block");
8432 Roo.get(document.body).on('click', function(){
8436 Roo.get(document.body).on('touchstart', function(){
8440 this.isApplied = true
8443 mask : function(form, target)
8447 this.target = target;
8449 if(!this.form.errorMask || !target.el){
8453 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8455 Roo.log(scrollable);
8457 var ot = this.target.el.calcOffsetsTo(scrollable);
8459 var scrollTo = ot[1] - this.form.maskOffset;
8461 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8463 scrollable.scrollTo('top', scrollTo);
8465 var box = this.target.el.getBox();
8467 var zIndex = Roo.bootstrap.Modal.zIndex++;
8470 this.maskEl.top.setStyle('position', 'absolute');
8471 this.maskEl.top.setStyle('z-index', zIndex);
8472 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8473 this.maskEl.top.setLeft(0);
8474 this.maskEl.top.setTop(0);
8475 this.maskEl.top.show();
8477 this.maskEl.left.setStyle('position', 'absolute');
8478 this.maskEl.left.setStyle('z-index', zIndex);
8479 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8480 this.maskEl.left.setLeft(0);
8481 this.maskEl.left.setTop(box.y - this.padding);
8482 this.maskEl.left.show();
8484 this.maskEl.bottom.setStyle('position', 'absolute');
8485 this.maskEl.bottom.setStyle('z-index', zIndex);
8486 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8487 this.maskEl.bottom.setLeft(0);
8488 this.maskEl.bottom.setTop(box.bottom + this.padding);
8489 this.maskEl.bottom.show();
8491 this.maskEl.right.setStyle('position', 'absolute');
8492 this.maskEl.right.setStyle('z-index', zIndex);
8493 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8494 this.maskEl.right.setLeft(box.right + this.padding);
8495 this.maskEl.right.setTop(box.y - this.padding);
8496 this.maskEl.right.show();
8498 this.toolTip.bindEl = this.target.el;
8500 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8502 var tip = this.target.blankText;
8504 if(this.target.getValue() !== '' ) {
8506 if (this.target.invalidText.length) {
8507 tip = this.target.invalidText;
8508 } else if (this.target.regexText.length){
8509 tip = this.target.regexText;
8513 this.toolTip.show(tip);
8515 this.intervalID = window.setInterval(function() {
8516 Roo.bootstrap.Form.popover.unmask();
8519 window.onwheel = function(){ return false;};
8521 (function(){ this.isMasked = true; }).defer(500, this);
8527 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8531 this.maskEl.top.setStyle('position', 'absolute');
8532 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8533 this.maskEl.top.hide();
8535 this.maskEl.left.setStyle('position', 'absolute');
8536 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8537 this.maskEl.left.hide();
8539 this.maskEl.bottom.setStyle('position', 'absolute');
8540 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8541 this.maskEl.bottom.hide();
8543 this.maskEl.right.setStyle('position', 'absolute');
8544 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8545 this.maskEl.right.hide();
8547 this.toolTip.hide();
8549 this.toolTip.el.hide();
8551 window.onwheel = function(){ return true;};
8553 if(this.intervalID){
8554 window.clearInterval(this.intervalID);
8555 this.intervalID = false;
8558 this.isMasked = false;
8568 * Ext JS Library 1.1.1
8569 * Copyright(c) 2006-2007, Ext JS, LLC.
8571 * Originally Released Under LGPL - original licence link has changed is not relivant.
8574 * <script type="text/javascript">
8577 * @class Roo.form.VTypes
8578 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8581 Roo.form.VTypes = function(){
8582 // closure these in so they are only created once.
8583 var alpha = /^[a-zA-Z_]+$/;
8584 var alphanum = /^[a-zA-Z0-9_]+$/;
8585 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8586 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8588 // All these messages and functions are configurable
8591 * The function used to validate email addresses
8592 * @param {String} value The email address
8594 'email' : function(v){
8595 return email.test(v);
8598 * The error text to display when the email validation function returns false
8601 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8603 * The keystroke filter mask to be applied on email input
8606 'emailMask' : /[a-z0-9_\.\-@]/i,
8609 * The function used to validate URLs
8610 * @param {String} value The URL
8612 'url' : function(v){
8616 * The error text to display when the url validation function returns false
8619 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8622 * The function used to validate alpha values
8623 * @param {String} value The value
8625 'alpha' : function(v){
8626 return alpha.test(v);
8629 * The error text to display when the alpha validation function returns false
8632 'alphaText' : 'This field should only contain letters and _',
8634 * The keystroke filter mask to be applied on alpha input
8637 'alphaMask' : /[a-z_]/i,
8640 * The function used to validate alphanumeric values
8641 * @param {String} value The value
8643 'alphanum' : function(v){
8644 return alphanum.test(v);
8647 * The error text to display when the alphanumeric validation function returns false
8650 'alphanumText' : 'This field should only contain letters, numbers and _',
8652 * The keystroke filter mask to be applied on alphanumeric input
8655 'alphanumMask' : /[a-z0-9_]/i
8665 * @class Roo.bootstrap.Input
8666 * @extends Roo.bootstrap.Component
8667 * Bootstrap Input class
8668 * @cfg {Boolean} disabled is it disabled
8669 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8670 * @cfg {String} name name of the input
8671 * @cfg {string} fieldLabel - the label associated
8672 * @cfg {string} placeholder - placeholder to put in text.
8673 * @cfg {string} before - input group add on before
8674 * @cfg {string} after - input group add on after
8675 * @cfg {string} size - (lg|sm) or leave empty..
8676 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8677 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8678 * @cfg {Number} md colspan out of 12 for computer-sized screens
8679 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8680 * @cfg {string} value default value of the input
8681 * @cfg {Number} labelWidth set the width of label
8682 * @cfg {Number} labellg set the width of label (1-12)
8683 * @cfg {Number} labelmd set the width of label (1-12)
8684 * @cfg {Number} labelsm set the width of label (1-12)
8685 * @cfg {Number} labelxs set the width of label (1-12)
8686 * @cfg {String} labelAlign (top|left)
8687 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8688 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8689 * @cfg {String} indicatorpos (left|right) default left
8690 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8691 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8693 * @cfg {String} align (left|center|right) Default left
8694 * @cfg {Boolean} forceFeedback (true|false) Default false
8697 * Create a new Input
8698 * @param {Object} config The config object
8701 Roo.bootstrap.Input = function(config){
8703 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8708 * Fires when this field receives input focus.
8709 * @param {Roo.form.Field} this
8714 * Fires when this field loses input focus.
8715 * @param {Roo.form.Field} this
8720 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8721 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8722 * @param {Roo.form.Field} this
8723 * @param {Roo.EventObject} e The event object
8728 * Fires just before the field blurs if the field value has changed.
8729 * @param {Roo.form.Field} this
8730 * @param {Mixed} newValue The new value
8731 * @param {Mixed} oldValue The original value
8736 * Fires after the field has been marked as invalid.
8737 * @param {Roo.form.Field} this
8738 * @param {String} msg The validation message
8743 * Fires after the field has been validated with no errors.
8744 * @param {Roo.form.Field} this
8749 * Fires after the key up
8750 * @param {Roo.form.Field} this
8751 * @param {Roo.EventObject} e The event Object
8757 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8759 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8760 automatic validation (defaults to "keyup").
8762 validationEvent : "keyup",
8764 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8766 validateOnBlur : true,
8768 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8770 validationDelay : 250,
8772 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8774 focusClass : "x-form-focus", // not needed???
8778 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8780 invalidClass : "has-warning",
8783 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8785 validClass : "has-success",
8788 * @cfg {Boolean} hasFeedback (true|false) default true
8793 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8795 invalidFeedbackClass : "glyphicon-warning-sign",
8798 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8800 validFeedbackClass : "glyphicon-ok",
8803 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8805 selectOnFocus : false,
8808 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8812 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8817 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8819 disableKeyFilter : false,
8822 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8826 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8830 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8832 blankText : "Please complete this mandatory field",
8835 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8839 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8841 maxLength : Number.MAX_VALUE,
8843 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8845 minLengthText : "The minimum length for this field is {0}",
8847 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8849 maxLengthText : "The maximum length for this field is {0}",
8853 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8854 * If available, this function will be called only after the basic validators all return true, and will be passed the
8855 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8859 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8860 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8861 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8865 * @cfg {String} regexText -- Depricated - use Invalid Text
8870 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8876 autocomplete: false,
8895 formatedValue : false,
8896 forceFeedback : false,
8898 indicatorpos : 'left',
8908 parentLabelAlign : function()
8911 while (parent.parent()) {
8912 parent = parent.parent();
8913 if (typeof(parent.labelAlign) !='undefined') {
8914 return parent.labelAlign;
8921 getAutoCreate : function()
8923 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8929 if(this.inputType != 'hidden'){
8930 cfg.cls = 'form-group' //input-group
8936 type : this.inputType,
8938 cls : 'form-control',
8939 placeholder : this.placeholder || '',
8940 autocomplete : this.autocomplete || 'new-password'
8943 if(this.capture.length){
8944 input.capture = this.capture;
8947 if(this.accept.length){
8948 input.accept = this.accept + "/*";
8952 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8955 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8956 input.maxLength = this.maxLength;
8959 if (this.disabled) {
8960 input.disabled=true;
8963 if (this.readOnly) {
8964 input.readonly=true;
8968 input.name = this.name;
8972 input.cls += ' input-' + this.size;
8976 ['xs','sm','md','lg'].map(function(size){
8977 if (settings[size]) {
8978 cfg.cls += ' col-' + size + '-' + settings[size];
8982 var inputblock = input;
8986 cls: 'glyphicon form-control-feedback'
8989 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8992 cls : 'has-feedback',
9000 if (this.before || this.after) {
9003 cls : 'input-group',
9007 if (this.before && typeof(this.before) == 'string') {
9009 inputblock.cn.push({
9011 cls : 'roo-input-before input-group-addon',
9015 if (this.before && typeof(this.before) == 'object') {
9016 this.before = Roo.factory(this.before);
9018 inputblock.cn.push({
9020 cls : 'roo-input-before input-group-' +
9021 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9025 inputblock.cn.push(input);
9027 if (this.after && typeof(this.after) == 'string') {
9028 inputblock.cn.push({
9030 cls : 'roo-input-after input-group-addon',
9034 if (this.after && typeof(this.after) == 'object') {
9035 this.after = Roo.factory(this.after);
9037 inputblock.cn.push({
9039 cls : 'roo-input-after input-group-' +
9040 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9044 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9045 inputblock.cls += ' has-feedback';
9046 inputblock.cn.push(feedback);
9050 if (align ==='left' && this.fieldLabel.length) {
9052 cfg.cls += ' roo-form-group-label-left';
9057 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9058 tooltip : 'This field is required'
9063 cls : 'control-label',
9064 html : this.fieldLabel
9075 var labelCfg = cfg.cn[1];
9076 var contentCfg = cfg.cn[2];
9078 if(this.indicatorpos == 'right'){
9083 cls : 'control-label',
9087 html : this.fieldLabel
9091 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9092 tooltip : 'This field is required'
9105 labelCfg = cfg.cn[0];
9106 contentCfg = cfg.cn[1];
9110 if(this.labelWidth > 12){
9111 labelCfg.style = "width: " + this.labelWidth + 'px';
9114 if(this.labelWidth < 13 && this.labelmd == 0){
9115 this.labelmd = this.labelWidth;
9118 if(this.labellg > 0){
9119 labelCfg.cls += ' col-lg-' + this.labellg;
9120 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9123 if(this.labelmd > 0){
9124 labelCfg.cls += ' col-md-' + this.labelmd;
9125 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9128 if(this.labelsm > 0){
9129 labelCfg.cls += ' col-sm-' + this.labelsm;
9130 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9133 if(this.labelxs > 0){
9134 labelCfg.cls += ' col-xs-' + this.labelxs;
9135 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9139 } else if ( this.fieldLabel.length) {
9144 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9145 tooltip : 'This field is required'
9149 //cls : 'input-group-addon',
9150 html : this.fieldLabel
9158 if(this.indicatorpos == 'right'){
9163 //cls : 'input-group-addon',
9164 html : this.fieldLabel
9169 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9170 tooltip : 'This field is required'
9190 if (this.parentType === 'Navbar' && this.parent().bar) {
9191 cfg.cls += ' navbar-form';
9194 if (this.parentType === 'NavGroup') {
9195 cfg.cls += ' navbar-form';
9203 * return the real input element.
9205 inputEl: function ()
9207 return this.el.select('input.form-control',true).first();
9210 tooltipEl : function()
9212 return this.inputEl();
9215 indicatorEl : function()
9217 var indicator = this.el.select('i.roo-required-indicator',true).first();
9227 setDisabled : function(v)
9229 var i = this.inputEl().dom;
9231 i.removeAttribute('disabled');
9235 i.setAttribute('disabled','true');
9237 initEvents : function()
9240 this.inputEl().on("keydown" , this.fireKey, this);
9241 this.inputEl().on("focus", this.onFocus, this);
9242 this.inputEl().on("blur", this.onBlur, this);
9244 this.inputEl().relayEvent('keyup', this);
9246 this.indicator = this.indicatorEl();
9249 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9252 // reference to original value for reset
9253 this.originalValue = this.getValue();
9254 //Roo.form.TextField.superclass.initEvents.call(this);
9255 if(this.validationEvent == 'keyup'){
9256 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9257 this.inputEl().on('keyup', this.filterValidation, this);
9259 else if(this.validationEvent !== false){
9260 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9263 if(this.selectOnFocus){
9264 this.on("focus", this.preFocus, this);
9267 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9268 this.inputEl().on("keypress", this.filterKeys, this);
9270 this.inputEl().relayEvent('keypress', this);
9273 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9274 this.el.on("click", this.autoSize, this);
9277 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9278 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9281 if (typeof(this.before) == 'object') {
9282 this.before.render(this.el.select('.roo-input-before',true).first());
9284 if (typeof(this.after) == 'object') {
9285 this.after.render(this.el.select('.roo-input-after',true).first());
9288 this.inputEl().on('change', this.onChange, this);
9291 filterValidation : function(e){
9292 if(!e.isNavKeyPress()){
9293 this.validationTask.delay(this.validationDelay);
9297 * Validates the field value
9298 * @return {Boolean} True if the value is valid, else false
9300 validate : function(){
9301 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9302 if(this.disabled || this.validateValue(this.getRawValue())){
9313 * Validates a value according to the field's validation rules and marks the field as invalid
9314 * if the validation fails
9315 * @param {Mixed} value The value to validate
9316 * @return {Boolean} True if the value is valid, else false
9318 validateValue : function(value)
9320 if(this.getVisibilityEl().hasClass('hidden')){
9324 if(value.length < 1) { // if it's blank
9325 if(this.allowBlank){
9331 if(value.length < this.minLength){
9334 if(value.length > this.maxLength){
9338 var vt = Roo.form.VTypes;
9339 if(!vt[this.vtype](value, this)){
9343 if(typeof this.validator == "function"){
9344 var msg = this.validator(value);
9348 if (typeof(msg) == 'string') {
9349 this.invalidText = msg;
9353 if(this.regex && !this.regex.test(value)){
9361 fireKey : function(e){
9362 //Roo.log('field ' + e.getKey());
9363 if(e.isNavKeyPress()){
9364 this.fireEvent("specialkey", this, e);
9367 focus : function (selectText){
9369 this.inputEl().focus();
9370 if(selectText === true){
9371 this.inputEl().dom.select();
9377 onFocus : function(){
9378 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9379 // this.el.addClass(this.focusClass);
9382 this.hasFocus = true;
9383 this.startValue = this.getValue();
9384 this.fireEvent("focus", this);
9388 beforeBlur : Roo.emptyFn,
9392 onBlur : function(){
9394 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9395 //this.el.removeClass(this.focusClass);
9397 this.hasFocus = false;
9398 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9401 var v = this.getValue();
9402 if(String(v) !== String(this.startValue)){
9403 this.fireEvent('change', this, v, this.startValue);
9405 this.fireEvent("blur", this);
9408 onChange : function(e)
9410 var v = this.getValue();
9411 if(String(v) !== String(this.startValue)){
9412 this.fireEvent('change', this, v, this.startValue);
9418 * Resets the current field value to the originally loaded value and clears any validation messages
9421 this.setValue(this.originalValue);
9425 * Returns the name of the field
9426 * @return {Mixed} name The name field
9428 getName: function(){
9432 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9433 * @return {Mixed} value The field value
9435 getValue : function(){
9437 var v = this.inputEl().getValue();
9442 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9443 * @return {Mixed} value The field value
9445 getRawValue : function(){
9446 var v = this.inputEl().getValue();
9452 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9453 * @param {Mixed} value The value to set
9455 setRawValue : function(v){
9456 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9459 selectText : function(start, end){
9460 var v = this.getRawValue();
9462 start = start === undefined ? 0 : start;
9463 end = end === undefined ? v.length : end;
9464 var d = this.inputEl().dom;
9465 if(d.setSelectionRange){
9466 d.setSelectionRange(start, end);
9467 }else if(d.createTextRange){
9468 var range = d.createTextRange();
9469 range.moveStart("character", start);
9470 range.moveEnd("character", v.length-end);
9477 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9478 * @param {Mixed} value The value to set
9480 setValue : function(v){
9483 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9489 processValue : function(value){
9490 if(this.stripCharsRe){
9491 var newValue = value.replace(this.stripCharsRe, '');
9492 if(newValue !== value){
9493 this.setRawValue(newValue);
9500 preFocus : function(){
9502 if(this.selectOnFocus){
9503 this.inputEl().dom.select();
9506 filterKeys : function(e){
9508 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9511 var c = e.getCharCode(), cc = String.fromCharCode(c);
9512 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9515 if(!this.maskRe.test(cc)){
9520 * Clear any invalid styles/messages for this field
9522 clearInvalid : function(){
9524 if(!this.el || this.preventMark){ // not rendered
9529 this.el.removeClass(this.invalidClass);
9531 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9533 var feedback = this.el.select('.form-control-feedback', true).first();
9536 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9542 this.indicator.removeClass('visible');
9543 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9546 this.fireEvent('valid', this);
9550 * Mark this field as valid
9552 markValid : function()
9554 if(!this.el || this.preventMark){ // not rendered...
9558 this.el.removeClass([this.invalidClass, this.validClass]);
9560 var feedback = this.el.select('.form-control-feedback', true).first();
9563 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9567 this.indicator.removeClass('visible');
9568 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9575 if(this.allowBlank && !this.getRawValue().length){
9579 this.el.addClass(this.validClass);
9581 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9583 var feedback = this.el.select('.form-control-feedback', true).first();
9586 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9587 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9592 this.fireEvent('valid', this);
9596 * Mark this field as invalid
9597 * @param {String} msg The validation message
9599 markInvalid : function(msg)
9601 if(!this.el || this.preventMark){ // not rendered
9605 this.el.removeClass([this.invalidClass, this.validClass]);
9607 var feedback = this.el.select('.form-control-feedback', true).first();
9610 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9617 if(this.allowBlank && !this.getRawValue().length){
9622 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9623 this.indicator.addClass('visible');
9626 this.el.addClass(this.invalidClass);
9628 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9630 var feedback = this.el.select('.form-control-feedback', true).first();
9633 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9635 if(this.getValue().length || this.forceFeedback){
9636 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9643 this.fireEvent('invalid', this, msg);
9646 SafariOnKeyDown : function(event)
9648 // this is a workaround for a password hang bug on chrome/ webkit.
9649 if (this.inputEl().dom.type != 'password') {
9653 var isSelectAll = false;
9655 if(this.inputEl().dom.selectionEnd > 0){
9656 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9658 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9659 event.preventDefault();
9664 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9666 event.preventDefault();
9667 // this is very hacky as keydown always get's upper case.
9669 var cc = String.fromCharCode(event.getCharCode());
9670 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9674 adjustWidth : function(tag, w){
9675 tag = tag.toLowerCase();
9676 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9677 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9681 if(tag == 'textarea'){
9684 }else if(Roo.isOpera){
9688 if(tag == 'textarea'){
9696 setFieldLabel : function(v)
9703 var ar = this.el.select('label > span',true);
9705 if (ar.elements.length) {
9706 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9707 this.fieldLabel = v;
9711 var br = this.el.select('label',true);
9713 if(br.elements.length) {
9714 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9715 this.fieldLabel = v;
9719 Roo.log('Cannot Found any of label > span || label in input');
9723 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9724 this.fieldLabel = v;
9739 * @class Roo.bootstrap.TextArea
9740 * @extends Roo.bootstrap.Input
9741 * Bootstrap TextArea class
9742 * @cfg {Number} cols Specifies the visible width of a text area
9743 * @cfg {Number} rows Specifies the visible number of lines in a text area
9744 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9745 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9746 * @cfg {string} html text
9749 * Create a new TextArea
9750 * @param {Object} config The config object
9753 Roo.bootstrap.TextArea = function(config){
9754 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9758 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9768 getAutoCreate : function(){
9770 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9776 if(this.inputType != 'hidden'){
9777 cfg.cls = 'form-group' //input-group
9785 value : this.value || '',
9786 html: this.html || '',
9787 cls : 'form-control',
9788 placeholder : this.placeholder || ''
9792 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9793 input.maxLength = this.maxLength;
9797 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9801 input.cols = this.cols;
9804 if (this.readOnly) {
9805 input.readonly = true;
9809 input.name = this.name;
9813 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9817 ['xs','sm','md','lg'].map(function(size){
9818 if (settings[size]) {
9819 cfg.cls += ' col-' + size + '-' + settings[size];
9823 var inputblock = input;
9825 if(this.hasFeedback && !this.allowBlank){
9829 cls: 'glyphicon form-control-feedback'
9833 cls : 'has-feedback',
9842 if (this.before || this.after) {
9845 cls : 'input-group',
9849 inputblock.cn.push({
9851 cls : 'input-group-addon',
9856 inputblock.cn.push(input);
9858 if(this.hasFeedback && !this.allowBlank){
9859 inputblock.cls += ' has-feedback';
9860 inputblock.cn.push(feedback);
9864 inputblock.cn.push({
9866 cls : 'input-group-addon',
9873 if (align ==='left' && this.fieldLabel.length) {
9878 cls : 'control-label',
9879 html : this.fieldLabel
9890 if(this.labelWidth > 12){
9891 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9894 if(this.labelWidth < 13 && this.labelmd == 0){
9895 this.labelmd = this.labelWidth;
9898 if(this.labellg > 0){
9899 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9900 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9903 if(this.labelmd > 0){
9904 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9905 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9908 if(this.labelsm > 0){
9909 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9910 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9913 if(this.labelxs > 0){
9914 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9915 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9918 } else if ( this.fieldLabel.length) {
9923 //cls : 'input-group-addon',
9924 html : this.fieldLabel
9942 if (this.disabled) {
9943 input.disabled=true;
9950 * return the real textarea element.
9952 inputEl: function ()
9954 return this.el.select('textarea.form-control',true).first();
9958 * Clear any invalid styles/messages for this field
9960 clearInvalid : function()
9963 if(!this.el || this.preventMark){ // not rendered
9967 var label = this.el.select('label', true).first();
9968 var icon = this.el.select('i.fa-star', true).first();
9974 this.el.removeClass(this.invalidClass);
9976 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9978 var feedback = this.el.select('.form-control-feedback', true).first();
9981 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9986 this.fireEvent('valid', this);
9990 * Mark this field as valid
9992 markValid : function()
9994 if(!this.el || this.preventMark){ // not rendered
9998 this.el.removeClass([this.invalidClass, this.validClass]);
10000 var feedback = this.el.select('.form-control-feedback', true).first();
10003 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10006 if(this.disabled || this.allowBlank){
10010 var label = this.el.select('label', true).first();
10011 var icon = this.el.select('i.fa-star', true).first();
10017 this.el.addClass(this.validClass);
10019 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10021 var feedback = this.el.select('.form-control-feedback', true).first();
10024 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10025 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10030 this.fireEvent('valid', this);
10034 * Mark this field as invalid
10035 * @param {String} msg The validation message
10037 markInvalid : function(msg)
10039 if(!this.el || this.preventMark){ // not rendered
10043 this.el.removeClass([this.invalidClass, this.validClass]);
10045 var feedback = this.el.select('.form-control-feedback', true).first();
10048 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10051 if(this.disabled || this.allowBlank){
10055 var label = this.el.select('label', true).first();
10056 var icon = this.el.select('i.fa-star', true).first();
10058 if(!this.getValue().length && label && !icon){
10059 this.el.createChild({
10061 cls : 'text-danger fa fa-lg fa-star',
10062 tooltip : 'This field is required',
10063 style : 'margin-right:5px;'
10067 this.el.addClass(this.invalidClass);
10069 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10071 var feedback = this.el.select('.form-control-feedback', true).first();
10074 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10076 if(this.getValue().length || this.forceFeedback){
10077 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10084 this.fireEvent('invalid', this, msg);
10092 * trigger field - base class for combo..
10097 * @class Roo.bootstrap.TriggerField
10098 * @extends Roo.bootstrap.Input
10099 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10100 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10101 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10102 * for which you can provide a custom implementation. For example:
10104 var trigger = new Roo.bootstrap.TriggerField();
10105 trigger.onTriggerClick = myTriggerFn;
10106 trigger.applyTo('my-field');
10109 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10110 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10111 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10112 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10113 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10116 * Create a new TriggerField.
10117 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10118 * to the base TextField)
10120 Roo.bootstrap.TriggerField = function(config){
10121 this.mimicing = false;
10122 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10125 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10127 * @cfg {String} triggerClass A CSS class to apply to the trigger
10130 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10135 * @cfg {Boolean} removable (true|false) special filter default false
10139 /** @cfg {Boolean} grow @hide */
10140 /** @cfg {Number} growMin @hide */
10141 /** @cfg {Number} growMax @hide */
10147 autoSize: Roo.emptyFn,
10151 deferHeight : true,
10154 actionMode : 'wrap',
10159 getAutoCreate : function(){
10161 var align = this.labelAlign || this.parentLabelAlign();
10166 cls: 'form-group' //input-group
10173 type : this.inputType,
10174 cls : 'form-control',
10175 autocomplete: 'new-password',
10176 placeholder : this.placeholder || ''
10180 input.name = this.name;
10183 input.cls += ' input-' + this.size;
10186 if (this.disabled) {
10187 input.disabled=true;
10190 var inputblock = input;
10192 if(this.hasFeedback && !this.allowBlank){
10196 cls: 'glyphicon form-control-feedback'
10199 if(this.removable && !this.editable && !this.tickable){
10201 cls : 'has-feedback',
10207 cls : 'roo-combo-removable-btn close'
10214 cls : 'has-feedback',
10223 if(this.removable && !this.editable && !this.tickable){
10225 cls : 'roo-removable',
10231 cls : 'roo-combo-removable-btn close'
10238 if (this.before || this.after) {
10241 cls : 'input-group',
10245 inputblock.cn.push({
10247 cls : 'input-group-addon',
10252 inputblock.cn.push(input);
10254 if(this.hasFeedback && !this.allowBlank){
10255 inputblock.cls += ' has-feedback';
10256 inputblock.cn.push(feedback);
10260 inputblock.cn.push({
10262 cls : 'input-group-addon',
10275 cls: 'form-hidden-field'
10289 cls: 'form-hidden-field'
10293 cls: 'roo-select2-choices',
10297 cls: 'roo-select2-search-field',
10310 cls: 'roo-select2-container input-group',
10315 // cls: 'typeahead typeahead-long dropdown-menu',
10316 // style: 'display:none'
10321 if(!this.multiple && this.showToggleBtn){
10327 if (this.caret != false) {
10330 cls: 'fa fa-' + this.caret
10337 cls : 'input-group-addon btn dropdown-toggle',
10342 cls: 'combobox-clear',
10356 combobox.cls += ' roo-select2-container-multi';
10359 if (align ==='left' && this.fieldLabel.length) {
10361 cfg.cls += ' roo-form-group-label-left';
10366 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10367 tooltip : 'This field is required'
10372 cls : 'control-label',
10373 html : this.fieldLabel
10385 var labelCfg = cfg.cn[1];
10386 var contentCfg = cfg.cn[2];
10388 if(this.indicatorpos == 'right'){
10393 cls : 'control-label',
10397 html : this.fieldLabel
10401 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10402 tooltip : 'This field is required'
10415 labelCfg = cfg.cn[0];
10416 contentCfg = cfg.cn[1];
10419 if(this.labelWidth > 12){
10420 labelCfg.style = "width: " + this.labelWidth + 'px';
10423 if(this.labelWidth < 13 && this.labelmd == 0){
10424 this.labelmd = this.labelWidth;
10427 if(this.labellg > 0){
10428 labelCfg.cls += ' col-lg-' + this.labellg;
10429 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10432 if(this.labelmd > 0){
10433 labelCfg.cls += ' col-md-' + this.labelmd;
10434 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10437 if(this.labelsm > 0){
10438 labelCfg.cls += ' col-sm-' + this.labelsm;
10439 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10442 if(this.labelxs > 0){
10443 labelCfg.cls += ' col-xs-' + this.labelxs;
10444 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10447 } else if ( this.fieldLabel.length) {
10448 // Roo.log(" label");
10452 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10453 tooltip : 'This field is required'
10457 //cls : 'input-group-addon',
10458 html : this.fieldLabel
10466 if(this.indicatorpos == 'right'){
10474 html : this.fieldLabel
10478 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10479 tooltip : 'This field is required'
10492 // Roo.log(" no label && no align");
10499 ['xs','sm','md','lg'].map(function(size){
10500 if (settings[size]) {
10501 cfg.cls += ' col-' + size + '-' + settings[size];
10512 onResize : function(w, h){
10513 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10514 // if(typeof w == 'number'){
10515 // var x = w - this.trigger.getWidth();
10516 // this.inputEl().setWidth(this.adjustWidth('input', x));
10517 // this.trigger.setStyle('left', x+'px');
10522 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10525 getResizeEl : function(){
10526 return this.inputEl();
10530 getPositionEl : function(){
10531 return this.inputEl();
10535 alignErrorIcon : function(){
10536 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10540 initEvents : function(){
10544 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10545 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10546 if(!this.multiple && this.showToggleBtn){
10547 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10548 if(this.hideTrigger){
10549 this.trigger.setDisplayed(false);
10551 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10555 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10558 if(this.removable && !this.editable && !this.tickable){
10559 var close = this.closeTriggerEl();
10562 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10563 close.on('click', this.removeBtnClick, this, close);
10567 //this.trigger.addClassOnOver('x-form-trigger-over');
10568 //this.trigger.addClassOnClick('x-form-trigger-click');
10571 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10575 closeTriggerEl : function()
10577 var close = this.el.select('.roo-combo-removable-btn', true).first();
10578 return close ? close : false;
10581 removeBtnClick : function(e, h, el)
10583 e.preventDefault();
10585 if(this.fireEvent("remove", this) !== false){
10587 this.fireEvent("afterremove", this)
10591 createList : function()
10593 this.list = Roo.get(document.body).createChild({
10595 cls: 'typeahead typeahead-long dropdown-menu',
10596 style: 'display:none'
10599 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10604 initTrigger : function(){
10609 onDestroy : function(){
10611 this.trigger.removeAllListeners();
10612 // this.trigger.remove();
10615 // this.wrap.remove();
10617 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10621 onFocus : function(){
10622 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10624 if(!this.mimicing){
10625 this.wrap.addClass('x-trigger-wrap-focus');
10626 this.mimicing = true;
10627 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10628 if(this.monitorTab){
10629 this.el.on("keydown", this.checkTab, this);
10636 checkTab : function(e){
10637 if(e.getKey() == e.TAB){
10638 this.triggerBlur();
10643 onBlur : function(){
10648 mimicBlur : function(e, t){
10650 if(!this.wrap.contains(t) && this.validateBlur()){
10651 this.triggerBlur();
10657 triggerBlur : function(){
10658 this.mimicing = false;
10659 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10660 if(this.monitorTab){
10661 this.el.un("keydown", this.checkTab, this);
10663 //this.wrap.removeClass('x-trigger-wrap-focus');
10664 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10668 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10669 validateBlur : function(e, t){
10674 onDisable : function(){
10675 this.inputEl().dom.disabled = true;
10676 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10678 // this.wrap.addClass('x-item-disabled');
10683 onEnable : function(){
10684 this.inputEl().dom.disabled = false;
10685 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10687 // this.el.removeClass('x-item-disabled');
10692 onShow : function(){
10693 var ae = this.getActionEl();
10696 ae.dom.style.display = '';
10697 ae.dom.style.visibility = 'visible';
10703 onHide : function(){
10704 var ae = this.getActionEl();
10705 ae.dom.style.display = 'none';
10709 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10710 * by an implementing function.
10712 * @param {EventObject} e
10714 onTriggerClick : Roo.emptyFn
10718 * Ext JS Library 1.1.1
10719 * Copyright(c) 2006-2007, Ext JS, LLC.
10721 * Originally Released Under LGPL - original licence link has changed is not relivant.
10724 * <script type="text/javascript">
10729 * @class Roo.data.SortTypes
10731 * Defines the default sorting (casting?) comparison functions used when sorting data.
10733 Roo.data.SortTypes = {
10735 * Default sort that does nothing
10736 * @param {Mixed} s The value being converted
10737 * @return {Mixed} The comparison value
10739 none : function(s){
10744 * The regular expression used to strip tags
10748 stripTagsRE : /<\/?[^>]+>/gi,
10751 * Strips all HTML tags to sort on text only
10752 * @param {Mixed} s The value being converted
10753 * @return {String} The comparison value
10755 asText : function(s){
10756 return String(s).replace(this.stripTagsRE, "");
10760 * Strips all HTML tags to sort on text only - Case insensitive
10761 * @param {Mixed} s The value being converted
10762 * @return {String} The comparison value
10764 asUCText : function(s){
10765 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10769 * Case insensitive string
10770 * @param {Mixed} s The value being converted
10771 * @return {String} The comparison value
10773 asUCString : function(s) {
10774 return String(s).toUpperCase();
10779 * @param {Mixed} s The value being converted
10780 * @return {Number} The comparison value
10782 asDate : function(s) {
10786 if(s instanceof Date){
10787 return s.getTime();
10789 return Date.parse(String(s));
10794 * @param {Mixed} s The value being converted
10795 * @return {Float} The comparison value
10797 asFloat : function(s) {
10798 var val = parseFloat(String(s).replace(/,/g, ""));
10807 * @param {Mixed} s The value being converted
10808 * @return {Number} The comparison value
10810 asInt : function(s) {
10811 var val = parseInt(String(s).replace(/,/g, ""));
10819 * Ext JS Library 1.1.1
10820 * Copyright(c) 2006-2007, Ext JS, LLC.
10822 * Originally Released Under LGPL - original licence link has changed is not relivant.
10825 * <script type="text/javascript">
10829 * @class Roo.data.Record
10830 * Instances of this class encapsulate both record <em>definition</em> information, and record
10831 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10832 * to access Records cached in an {@link Roo.data.Store} object.<br>
10834 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10835 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10838 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10840 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10841 * {@link #create}. The parameters are the same.
10842 * @param {Array} data An associative Array of data values keyed by the field name.
10843 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10844 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10845 * not specified an integer id is generated.
10847 Roo.data.Record = function(data, id){
10848 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10853 * Generate a constructor for a specific record layout.
10854 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10855 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10856 * Each field definition object may contain the following properties: <ul>
10857 * <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,
10858 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10859 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10860 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10861 * is being used, then this is a string containing the javascript expression to reference the data relative to
10862 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10863 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10864 * this may be omitted.</p></li>
10865 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10866 * <ul><li>auto (Default, implies no conversion)</li>
10871 * <li>date</li></ul></p></li>
10872 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10873 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10874 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10875 * by the Reader into an object that will be stored in the Record. It is passed the
10876 * following parameters:<ul>
10877 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10879 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10881 * <br>usage:<br><pre><code>
10882 var TopicRecord = Roo.data.Record.create(
10883 {name: 'title', mapping: 'topic_title'},
10884 {name: 'author', mapping: 'username'},
10885 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10886 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10887 {name: 'lastPoster', mapping: 'user2'},
10888 {name: 'excerpt', mapping: 'post_text'}
10891 var myNewRecord = new TopicRecord({
10892 title: 'Do my job please',
10895 lastPost: new Date(),
10896 lastPoster: 'Animal',
10897 excerpt: 'No way dude!'
10899 myStore.add(myNewRecord);
10904 Roo.data.Record.create = function(o){
10905 var f = function(){
10906 f.superclass.constructor.apply(this, arguments);
10908 Roo.extend(f, Roo.data.Record);
10909 var p = f.prototype;
10910 p.fields = new Roo.util.MixedCollection(false, function(field){
10913 for(var i = 0, len = o.length; i < len; i++){
10914 p.fields.add(new Roo.data.Field(o[i]));
10916 f.getField = function(name){
10917 return p.fields.get(name);
10922 Roo.data.Record.AUTO_ID = 1000;
10923 Roo.data.Record.EDIT = 'edit';
10924 Roo.data.Record.REJECT = 'reject';
10925 Roo.data.Record.COMMIT = 'commit';
10927 Roo.data.Record.prototype = {
10929 * Readonly flag - true if this record has been modified.
10938 join : function(store){
10939 this.store = store;
10943 * Set the named field to the specified value.
10944 * @param {String} name The name of the field to set.
10945 * @param {Object} value The value to set the field to.
10947 set : function(name, value){
10948 if(this.data[name] == value){
10952 if(!this.modified){
10953 this.modified = {};
10955 if(typeof this.modified[name] == 'undefined'){
10956 this.modified[name] = this.data[name];
10958 this.data[name] = value;
10959 if(!this.editing && this.store){
10960 this.store.afterEdit(this);
10965 * Get the value of the named field.
10966 * @param {String} name The name of the field to get the value of.
10967 * @return {Object} The value of the field.
10969 get : function(name){
10970 return this.data[name];
10974 beginEdit : function(){
10975 this.editing = true;
10976 this.modified = {};
10980 cancelEdit : function(){
10981 this.editing = false;
10982 delete this.modified;
10986 endEdit : function(){
10987 this.editing = false;
10988 if(this.dirty && this.store){
10989 this.store.afterEdit(this);
10994 * Usually called by the {@link Roo.data.Store} which owns the Record.
10995 * Rejects all changes made to the Record since either creation, or the last commit operation.
10996 * Modified fields are reverted to their original values.
10998 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10999 * of reject operations.
11001 reject : function(){
11002 var m = this.modified;
11004 if(typeof m[n] != "function"){
11005 this.data[n] = m[n];
11008 this.dirty = false;
11009 delete this.modified;
11010 this.editing = false;
11012 this.store.afterReject(this);
11017 * Usually called by the {@link Roo.data.Store} which owns the Record.
11018 * Commits all changes made to the Record since either creation, or the last commit operation.
11020 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11021 * of commit operations.
11023 commit : function(){
11024 this.dirty = false;
11025 delete this.modified;
11026 this.editing = false;
11028 this.store.afterCommit(this);
11033 hasError : function(){
11034 return this.error != null;
11038 clearError : function(){
11043 * Creates a copy of this record.
11044 * @param {String} id (optional) A new record id if you don't want to use this record's id
11047 copy : function(newId) {
11048 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11052 * Ext JS Library 1.1.1
11053 * Copyright(c) 2006-2007, Ext JS, LLC.
11055 * Originally Released Under LGPL - original licence link has changed is not relivant.
11058 * <script type="text/javascript">
11064 * @class Roo.data.Store
11065 * @extends Roo.util.Observable
11066 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11067 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11069 * 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
11070 * has no knowledge of the format of the data returned by the Proxy.<br>
11072 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11073 * instances from the data object. These records are cached and made available through accessor functions.
11075 * Creates a new Store.
11076 * @param {Object} config A config object containing the objects needed for the Store to access data,
11077 * and read the data into Records.
11079 Roo.data.Store = function(config){
11080 this.data = new Roo.util.MixedCollection(false);
11081 this.data.getKey = function(o){
11084 this.baseParams = {};
11086 this.paramNames = {
11091 "multisort" : "_multisort"
11094 if(config && config.data){
11095 this.inlineData = config.data;
11096 delete config.data;
11099 Roo.apply(this, config);
11101 if(this.reader){ // reader passed
11102 this.reader = Roo.factory(this.reader, Roo.data);
11103 this.reader.xmodule = this.xmodule || false;
11104 if(!this.recordType){
11105 this.recordType = this.reader.recordType;
11107 if(this.reader.onMetaChange){
11108 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11112 if(this.recordType){
11113 this.fields = this.recordType.prototype.fields;
11115 this.modified = [];
11119 * @event datachanged
11120 * Fires when the data cache has changed, and a widget which is using this Store
11121 * as a Record cache should refresh its view.
11122 * @param {Store} this
11124 datachanged : true,
11126 * @event metachange
11127 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11128 * @param {Store} this
11129 * @param {Object} meta The JSON metadata
11134 * Fires when Records have been added to the Store
11135 * @param {Store} this
11136 * @param {Roo.data.Record[]} records The array of Records added
11137 * @param {Number} index The index at which the record(s) were added
11142 * Fires when a Record has been removed from the Store
11143 * @param {Store} this
11144 * @param {Roo.data.Record} record The Record that was removed
11145 * @param {Number} index The index at which the record was removed
11150 * Fires when a Record has been updated
11151 * @param {Store} this
11152 * @param {Roo.data.Record} record The Record that was updated
11153 * @param {String} operation The update operation being performed. Value may be one of:
11155 Roo.data.Record.EDIT
11156 Roo.data.Record.REJECT
11157 Roo.data.Record.COMMIT
11163 * Fires when the data cache has been cleared.
11164 * @param {Store} this
11168 * @event beforeload
11169 * Fires before a request is made for a new data object. If the beforeload handler returns false
11170 * the load action will be canceled.
11171 * @param {Store} this
11172 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11176 * @event beforeloadadd
11177 * Fires after a new set of Records has been loaded.
11178 * @param {Store} this
11179 * @param {Roo.data.Record[]} records The Records that were loaded
11180 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11182 beforeloadadd : true,
11185 * Fires after a new set of Records has been loaded, before they are added to the store.
11186 * @param {Store} this
11187 * @param {Roo.data.Record[]} records The Records that were loaded
11188 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11189 * @params {Object} return from reader
11193 * @event loadexception
11194 * Fires if an exception occurs in the Proxy during loading.
11195 * Called with the signature of the Proxy's "loadexception" event.
11196 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11199 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11200 * @param {Object} load options
11201 * @param {Object} jsonData from your request (normally this contains the Exception)
11203 loadexception : true
11207 this.proxy = Roo.factory(this.proxy, Roo.data);
11208 this.proxy.xmodule = this.xmodule || false;
11209 this.relayEvents(this.proxy, ["loadexception"]);
11211 this.sortToggle = {};
11212 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11214 Roo.data.Store.superclass.constructor.call(this);
11216 if(this.inlineData){
11217 this.loadData(this.inlineData);
11218 delete this.inlineData;
11222 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11224 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11225 * without a remote query - used by combo/forms at present.
11229 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11232 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11235 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11236 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11239 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11240 * on any HTTP request
11243 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11246 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11250 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11251 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11253 remoteSort : false,
11256 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11257 * loaded or when a record is removed. (defaults to false).
11259 pruneModifiedRecords : false,
11262 lastOptions : null,
11265 * Add Records to the Store and fires the add event.
11266 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11268 add : function(records){
11269 records = [].concat(records);
11270 for(var i = 0, len = records.length; i < len; i++){
11271 records[i].join(this);
11273 var index = this.data.length;
11274 this.data.addAll(records);
11275 this.fireEvent("add", this, records, index);
11279 * Remove a Record from the Store and fires the remove event.
11280 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11282 remove : function(record){
11283 var index = this.data.indexOf(record);
11284 this.data.removeAt(index);
11286 if(this.pruneModifiedRecords){
11287 this.modified.remove(record);
11289 this.fireEvent("remove", this, record, index);
11293 * Remove all Records from the Store and fires the clear event.
11295 removeAll : function(){
11297 if(this.pruneModifiedRecords){
11298 this.modified = [];
11300 this.fireEvent("clear", this);
11304 * Inserts Records to the Store at the given index and fires the add event.
11305 * @param {Number} index The start index at which to insert the passed Records.
11306 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11308 insert : function(index, records){
11309 records = [].concat(records);
11310 for(var i = 0, len = records.length; i < len; i++){
11311 this.data.insert(index, records[i]);
11312 records[i].join(this);
11314 this.fireEvent("add", this, records, index);
11318 * Get the index within the cache of the passed Record.
11319 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11320 * @return {Number} The index of the passed Record. Returns -1 if not found.
11322 indexOf : function(record){
11323 return this.data.indexOf(record);
11327 * Get the index within the cache of the Record with the passed id.
11328 * @param {String} id The id of the Record to find.
11329 * @return {Number} The index of the Record. Returns -1 if not found.
11331 indexOfId : function(id){
11332 return this.data.indexOfKey(id);
11336 * Get the Record with the specified id.
11337 * @param {String} id The id of the Record to find.
11338 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11340 getById : function(id){
11341 return this.data.key(id);
11345 * Get the Record at the specified index.
11346 * @param {Number} index The index of the Record to find.
11347 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11349 getAt : function(index){
11350 return this.data.itemAt(index);
11354 * Returns a range of Records between specified indices.
11355 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11356 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11357 * @return {Roo.data.Record[]} An array of Records
11359 getRange : function(start, end){
11360 return this.data.getRange(start, end);
11364 storeOptions : function(o){
11365 o = Roo.apply({}, o);
11368 this.lastOptions = o;
11372 * Loads the Record cache from the configured Proxy using the configured Reader.
11374 * If using remote paging, then the first load call must specify the <em>start</em>
11375 * and <em>limit</em> properties in the options.params property to establish the initial
11376 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11378 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11379 * and this call will return before the new data has been loaded. Perform any post-processing
11380 * in a callback function, or in a "load" event handler.</strong>
11382 * @param {Object} options An object containing properties which control loading options:<ul>
11383 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11384 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11385 * passed the following arguments:<ul>
11386 * <li>r : Roo.data.Record[]</li>
11387 * <li>options: Options object from the load call</li>
11388 * <li>success: Boolean success indicator</li></ul></li>
11389 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11390 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11393 load : function(options){
11394 options = options || {};
11395 if(this.fireEvent("beforeload", this, options) !== false){
11396 this.storeOptions(options);
11397 var p = Roo.apply(options.params || {}, this.baseParams);
11398 // if meta was not loaded from remote source.. try requesting it.
11399 if (!this.reader.metaFromRemote) {
11400 p._requestMeta = 1;
11402 if(this.sortInfo && this.remoteSort){
11403 var pn = this.paramNames;
11404 p[pn["sort"]] = this.sortInfo.field;
11405 p[pn["dir"]] = this.sortInfo.direction;
11407 if (this.multiSort) {
11408 var pn = this.paramNames;
11409 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11412 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11417 * Reloads the Record cache from the configured Proxy using the configured Reader and
11418 * the options from the last load operation performed.
11419 * @param {Object} options (optional) An object containing properties which may override the options
11420 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11421 * the most recently used options are reused).
11423 reload : function(options){
11424 this.load(Roo.applyIf(options||{}, this.lastOptions));
11428 // Called as a callback by the Reader during a load operation.
11429 loadRecords : function(o, options, success){
11430 if(!o || success === false){
11431 if(success !== false){
11432 this.fireEvent("load", this, [], options, o);
11434 if(options.callback){
11435 options.callback.call(options.scope || this, [], options, false);
11439 // if data returned failure - throw an exception.
11440 if (o.success === false) {
11441 // show a message if no listener is registered.
11442 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11443 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11445 // loadmask wil be hooked into this..
11446 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11449 var r = o.records, t = o.totalRecords || r.length;
11451 this.fireEvent("beforeloadadd", this, r, options, o);
11453 if(!options || options.add !== true){
11454 if(this.pruneModifiedRecords){
11455 this.modified = [];
11457 for(var i = 0, len = r.length; i < len; i++){
11461 this.data = this.snapshot;
11462 delete this.snapshot;
11465 this.data.addAll(r);
11466 this.totalLength = t;
11468 this.fireEvent("datachanged", this);
11470 this.totalLength = Math.max(t, this.data.length+r.length);
11474 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11476 var e = new Roo.data.Record({});
11478 e.set(this.parent.displayField, this.parent.emptyTitle);
11479 e.set(this.parent.valueField, '');
11484 this.fireEvent("load", this, r, options, o);
11485 if(options.callback){
11486 options.callback.call(options.scope || this, r, options, true);
11492 * Loads data from a passed data block. A Reader which understands the format of the data
11493 * must have been configured in the constructor.
11494 * @param {Object} data The data block from which to read the Records. The format of the data expected
11495 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11496 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11498 loadData : function(o, append){
11499 var r = this.reader.readRecords(o);
11500 this.loadRecords(r, {add: append}, true);
11504 * Gets the number of cached records.
11506 * <em>If using paging, this may not be the total size of the dataset. If the data object
11507 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11508 * the data set size</em>
11510 getCount : function(){
11511 return this.data.length || 0;
11515 * Gets the total number of records in the dataset as returned by the server.
11517 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11518 * the dataset size</em>
11520 getTotalCount : function(){
11521 return this.totalLength || 0;
11525 * Returns the sort state of the Store as an object with two properties:
11527 field {String} The name of the field by which the Records are sorted
11528 direction {String} The sort order, "ASC" or "DESC"
11531 getSortState : function(){
11532 return this.sortInfo;
11536 applySort : function(){
11537 if(this.sortInfo && !this.remoteSort){
11538 var s = this.sortInfo, f = s.field;
11539 var st = this.fields.get(f).sortType;
11540 var fn = function(r1, r2){
11541 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11542 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11544 this.data.sort(s.direction, fn);
11545 if(this.snapshot && this.snapshot != this.data){
11546 this.snapshot.sort(s.direction, fn);
11552 * Sets the default sort column and order to be used by the next load operation.
11553 * @param {String} fieldName The name of the field to sort by.
11554 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11556 setDefaultSort : function(field, dir){
11557 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11561 * Sort the Records.
11562 * If remote sorting is used, the sort is performed on the server, and the cache is
11563 * reloaded. If local sorting is used, the cache is sorted internally.
11564 * @param {String} fieldName The name of the field to sort by.
11565 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11567 sort : function(fieldName, dir){
11568 var f = this.fields.get(fieldName);
11570 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11572 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11573 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11578 this.sortToggle[f.name] = dir;
11579 this.sortInfo = {field: f.name, direction: dir};
11580 if(!this.remoteSort){
11582 this.fireEvent("datachanged", this);
11584 this.load(this.lastOptions);
11589 * Calls the specified function for each of the Records in the cache.
11590 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11591 * Returning <em>false</em> aborts and exits the iteration.
11592 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11594 each : function(fn, scope){
11595 this.data.each(fn, scope);
11599 * Gets all records modified since the last commit. Modified records are persisted across load operations
11600 * (e.g., during paging).
11601 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11603 getModifiedRecords : function(){
11604 return this.modified;
11608 createFilterFn : function(property, value, anyMatch){
11609 if(!value.exec){ // not a regex
11610 value = String(value);
11611 if(value.length == 0){
11614 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11616 return function(r){
11617 return value.test(r.data[property]);
11622 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11623 * @param {String} property A field on your records
11624 * @param {Number} start The record index to start at (defaults to 0)
11625 * @param {Number} end The last record index to include (defaults to length - 1)
11626 * @return {Number} The sum
11628 sum : function(property, start, end){
11629 var rs = this.data.items, v = 0;
11630 start = start || 0;
11631 end = (end || end === 0) ? end : rs.length-1;
11633 for(var i = start; i <= end; i++){
11634 v += (rs[i].data[property] || 0);
11640 * Filter the records by a specified property.
11641 * @param {String} field A field on your records
11642 * @param {String/RegExp} value Either a string that the field
11643 * should start with or a RegExp to test against the field
11644 * @param {Boolean} anyMatch True to match any part not just the beginning
11646 filter : function(property, value, anyMatch){
11647 var fn = this.createFilterFn(property, value, anyMatch);
11648 return fn ? this.filterBy(fn) : this.clearFilter();
11652 * Filter by a function. The specified function will be called with each
11653 * record in this data source. If the function returns true the record is included,
11654 * otherwise it is filtered.
11655 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11656 * @param {Object} scope (optional) The scope of the function (defaults to this)
11658 filterBy : function(fn, scope){
11659 this.snapshot = this.snapshot || this.data;
11660 this.data = this.queryBy(fn, scope||this);
11661 this.fireEvent("datachanged", this);
11665 * Query the records by a specified property.
11666 * @param {String} field A field on your records
11667 * @param {String/RegExp} value Either a string that the field
11668 * should start with or a RegExp to test against the field
11669 * @param {Boolean} anyMatch True to match any part not just the beginning
11670 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11672 query : function(property, value, anyMatch){
11673 var fn = this.createFilterFn(property, value, anyMatch);
11674 return fn ? this.queryBy(fn) : this.data.clone();
11678 * Query by a function. The specified function will be called with each
11679 * record in this data source. If the function returns true the record is included
11681 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11682 * @param {Object} scope (optional) The scope of the function (defaults to this)
11683 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11685 queryBy : function(fn, scope){
11686 var data = this.snapshot || this.data;
11687 return data.filterBy(fn, scope||this);
11691 * Collects unique values for a particular dataIndex from this store.
11692 * @param {String} dataIndex The property to collect
11693 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11694 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11695 * @return {Array} An array of the unique values
11697 collect : function(dataIndex, allowNull, bypassFilter){
11698 var d = (bypassFilter === true && this.snapshot) ?
11699 this.snapshot.items : this.data.items;
11700 var v, sv, r = [], l = {};
11701 for(var i = 0, len = d.length; i < len; i++){
11702 v = d[i].data[dataIndex];
11704 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11713 * Revert to a view of the Record cache with no filtering applied.
11714 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11716 clearFilter : function(suppressEvent){
11717 if(this.snapshot && this.snapshot != this.data){
11718 this.data = this.snapshot;
11719 delete this.snapshot;
11720 if(suppressEvent !== true){
11721 this.fireEvent("datachanged", this);
11727 afterEdit : function(record){
11728 if(this.modified.indexOf(record) == -1){
11729 this.modified.push(record);
11731 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11735 afterReject : function(record){
11736 this.modified.remove(record);
11737 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11741 afterCommit : function(record){
11742 this.modified.remove(record);
11743 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11747 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11748 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11750 commitChanges : function(){
11751 var m = this.modified.slice(0);
11752 this.modified = [];
11753 for(var i = 0, len = m.length; i < len; i++){
11759 * Cancel outstanding changes on all changed records.
11761 rejectChanges : function(){
11762 var m = this.modified.slice(0);
11763 this.modified = [];
11764 for(var i = 0, len = m.length; i < len; i++){
11769 onMetaChange : function(meta, rtype, o){
11770 this.recordType = rtype;
11771 this.fields = rtype.prototype.fields;
11772 delete this.snapshot;
11773 this.sortInfo = meta.sortInfo || this.sortInfo;
11774 this.modified = [];
11775 this.fireEvent('metachange', this, this.reader.meta);
11778 moveIndex : function(data, type)
11780 var index = this.indexOf(data);
11782 var newIndex = index + type;
11786 this.insert(newIndex, data);
11791 * Ext JS Library 1.1.1
11792 * Copyright(c) 2006-2007, Ext JS, LLC.
11794 * Originally Released Under LGPL - original licence link has changed is not relivant.
11797 * <script type="text/javascript">
11801 * @class Roo.data.SimpleStore
11802 * @extends Roo.data.Store
11803 * Small helper class to make creating Stores from Array data easier.
11804 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11805 * @cfg {Array} fields An array of field definition objects, or field name strings.
11806 * @cfg {Array} data The multi-dimensional array of data
11808 * @param {Object} config
11810 Roo.data.SimpleStore = function(config){
11811 Roo.data.SimpleStore.superclass.constructor.call(this, {
11813 reader: new Roo.data.ArrayReader({
11816 Roo.data.Record.create(config.fields)
11818 proxy : new Roo.data.MemoryProxy(config.data)
11822 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11824 * Ext JS Library 1.1.1
11825 * Copyright(c) 2006-2007, Ext JS, LLC.
11827 * Originally Released Under LGPL - original licence link has changed is not relivant.
11830 * <script type="text/javascript">
11835 * @extends Roo.data.Store
11836 * @class Roo.data.JsonStore
11837 * Small helper class to make creating Stores for JSON data easier. <br/>
11839 var store = new Roo.data.JsonStore({
11840 url: 'get-images.php',
11842 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11845 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11846 * JsonReader and HttpProxy (unless inline data is provided).</b>
11847 * @cfg {Array} fields An array of field definition objects, or field name strings.
11849 * @param {Object} config
11851 Roo.data.JsonStore = function(c){
11852 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11853 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11854 reader: new Roo.data.JsonReader(c, c.fields)
11857 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11859 * Ext JS Library 1.1.1
11860 * Copyright(c) 2006-2007, Ext JS, LLC.
11862 * Originally Released Under LGPL - original licence link has changed is not relivant.
11865 * <script type="text/javascript">
11869 Roo.data.Field = function(config){
11870 if(typeof config == "string"){
11871 config = {name: config};
11873 Roo.apply(this, config);
11876 this.type = "auto";
11879 var st = Roo.data.SortTypes;
11880 // named sortTypes are supported, here we look them up
11881 if(typeof this.sortType == "string"){
11882 this.sortType = st[this.sortType];
11885 // set default sortType for strings and dates
11886 if(!this.sortType){
11889 this.sortType = st.asUCString;
11892 this.sortType = st.asDate;
11895 this.sortType = st.none;
11900 var stripRe = /[\$,%]/g;
11902 // prebuilt conversion function for this field, instead of
11903 // switching every time we're reading a value
11905 var cv, dateFormat = this.dateFormat;
11910 cv = function(v){ return v; };
11913 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11917 return v !== undefined && v !== null && v !== '' ?
11918 parseInt(String(v).replace(stripRe, ""), 10) : '';
11923 return v !== undefined && v !== null && v !== '' ?
11924 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11929 cv = function(v){ return v === true || v === "true" || v == 1; };
11936 if(v instanceof Date){
11940 if(dateFormat == "timestamp"){
11941 return new Date(v*1000);
11943 return Date.parseDate(v, dateFormat);
11945 var parsed = Date.parse(v);
11946 return parsed ? new Date(parsed) : null;
11955 Roo.data.Field.prototype = {
11963 * Ext JS Library 1.1.1
11964 * Copyright(c) 2006-2007, Ext JS, LLC.
11966 * Originally Released Under LGPL - original licence link has changed is not relivant.
11969 * <script type="text/javascript">
11972 // Base class for reading structured data from a data source. This class is intended to be
11973 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11976 * @class Roo.data.DataReader
11977 * Base class for reading structured data from a data source. This class is intended to be
11978 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11981 Roo.data.DataReader = function(meta, recordType){
11985 this.recordType = recordType instanceof Array ?
11986 Roo.data.Record.create(recordType) : recordType;
11989 Roo.data.DataReader.prototype = {
11991 * Create an empty record
11992 * @param {Object} data (optional) - overlay some values
11993 * @return {Roo.data.Record} record created.
11995 newRow : function(d) {
11997 this.recordType.prototype.fields.each(function(c) {
11999 case 'int' : da[c.name] = 0; break;
12000 case 'date' : da[c.name] = new Date(); break;
12001 case 'float' : da[c.name] = 0.0; break;
12002 case 'boolean' : da[c.name] = false; break;
12003 default : da[c.name] = ""; break;
12007 return new this.recordType(Roo.apply(da, d));
12012 * Ext JS Library 1.1.1
12013 * Copyright(c) 2006-2007, Ext JS, LLC.
12015 * Originally Released Under LGPL - original licence link has changed is not relivant.
12018 * <script type="text/javascript">
12022 * @class Roo.data.DataProxy
12023 * @extends Roo.data.Observable
12024 * This class is an abstract base class for implementations which provide retrieval of
12025 * unformatted data objects.<br>
12027 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12028 * (of the appropriate type which knows how to parse the data object) to provide a block of
12029 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12031 * Custom implementations must implement the load method as described in
12032 * {@link Roo.data.HttpProxy#load}.
12034 Roo.data.DataProxy = function(){
12037 * @event beforeload
12038 * Fires before a network request is made to retrieve a data object.
12039 * @param {Object} This DataProxy object.
12040 * @param {Object} params The params parameter to the load function.
12045 * Fires before the load method's callback is called.
12046 * @param {Object} This DataProxy object.
12047 * @param {Object} o The data object.
12048 * @param {Object} arg The callback argument object passed to the load function.
12052 * @event loadexception
12053 * Fires if an Exception occurs during data retrieval.
12054 * @param {Object} This DataProxy object.
12055 * @param {Object} o The data object.
12056 * @param {Object} arg The callback argument object passed to the load function.
12057 * @param {Object} e The Exception.
12059 loadexception : true
12061 Roo.data.DataProxy.superclass.constructor.call(this);
12064 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12067 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12071 * Ext JS Library 1.1.1
12072 * Copyright(c) 2006-2007, Ext JS, LLC.
12074 * Originally Released Under LGPL - original licence link has changed is not relivant.
12077 * <script type="text/javascript">
12080 * @class Roo.data.MemoryProxy
12081 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12082 * to the Reader when its load method is called.
12084 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12086 Roo.data.MemoryProxy = function(data){
12090 Roo.data.MemoryProxy.superclass.constructor.call(this);
12094 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12097 * Load data from the requested source (in this case an in-memory
12098 * data object passed to the constructor), read the data object into
12099 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12100 * process that block using the passed callback.
12101 * @param {Object} params This parameter is not used by the MemoryProxy class.
12102 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12103 * object into a block of Roo.data.Records.
12104 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12105 * The function must be passed <ul>
12106 * <li>The Record block object</li>
12107 * <li>The "arg" argument from the load function</li>
12108 * <li>A boolean success indicator</li>
12110 * @param {Object} scope The scope in which to call the callback
12111 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12113 load : function(params, reader, callback, scope, arg){
12114 params = params || {};
12117 result = reader.readRecords(this.data);
12119 this.fireEvent("loadexception", this, arg, null, e);
12120 callback.call(scope, null, arg, false);
12123 callback.call(scope, result, arg, true);
12127 update : function(params, records){
12132 * Ext JS Library 1.1.1
12133 * Copyright(c) 2006-2007, Ext JS, LLC.
12135 * Originally Released Under LGPL - original licence link has changed is not relivant.
12138 * <script type="text/javascript">
12141 * @class Roo.data.HttpProxy
12142 * @extends Roo.data.DataProxy
12143 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12144 * configured to reference a certain URL.<br><br>
12146 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12147 * from which the running page was served.<br><br>
12149 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12151 * Be aware that to enable the browser to parse an XML document, the server must set
12152 * the Content-Type header in the HTTP response to "text/xml".
12154 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12155 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12156 * will be used to make the request.
12158 Roo.data.HttpProxy = function(conn){
12159 Roo.data.HttpProxy.superclass.constructor.call(this);
12160 // is conn a conn config or a real conn?
12162 this.useAjax = !conn || !conn.events;
12166 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12167 // thse are take from connection...
12170 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12173 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12174 * extra parameters to each request made by this object. (defaults to undefined)
12177 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12178 * to each request made by this object. (defaults to undefined)
12181 * @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)
12184 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12187 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12193 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12197 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12198 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12199 * a finer-grained basis than the DataProxy events.
12201 getConnection : function(){
12202 return this.useAjax ? Roo.Ajax : this.conn;
12206 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12207 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12208 * process that block using the passed callback.
12209 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12210 * for the request to the remote server.
12211 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12212 * object into a block of Roo.data.Records.
12213 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12214 * The function must be passed <ul>
12215 * <li>The Record block object</li>
12216 * <li>The "arg" argument from the load function</li>
12217 * <li>A boolean success indicator</li>
12219 * @param {Object} scope The scope in which to call the callback
12220 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12222 load : function(params, reader, callback, scope, arg){
12223 if(this.fireEvent("beforeload", this, params) !== false){
12225 params : params || {},
12227 callback : callback,
12232 callback : this.loadResponse,
12236 Roo.applyIf(o, this.conn);
12237 if(this.activeRequest){
12238 Roo.Ajax.abort(this.activeRequest);
12240 this.activeRequest = Roo.Ajax.request(o);
12242 this.conn.request(o);
12245 callback.call(scope||this, null, arg, false);
12250 loadResponse : function(o, success, response){
12251 delete this.activeRequest;
12253 this.fireEvent("loadexception", this, o, response);
12254 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12259 result = o.reader.read(response);
12261 this.fireEvent("loadexception", this, o, response, e);
12262 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12266 this.fireEvent("load", this, o, o.request.arg);
12267 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12271 update : function(dataSet){
12276 updateResponse : function(dataSet){
12281 * Ext JS Library 1.1.1
12282 * Copyright(c) 2006-2007, Ext JS, LLC.
12284 * Originally Released Under LGPL - original licence link has changed is not relivant.
12287 * <script type="text/javascript">
12291 * @class Roo.data.ScriptTagProxy
12292 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12293 * other than the originating domain of the running page.<br><br>
12295 * <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
12296 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12298 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12299 * source code that is used as the source inside a <script> tag.<br><br>
12301 * In order for the browser to process the returned data, the server must wrap the data object
12302 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12303 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12304 * depending on whether the callback name was passed:
12307 boolean scriptTag = false;
12308 String cb = request.getParameter("callback");
12311 response.setContentType("text/javascript");
12313 response.setContentType("application/x-json");
12315 Writer out = response.getWriter();
12317 out.write(cb + "(");
12319 out.print(dataBlock.toJsonString());
12326 * @param {Object} config A configuration object.
12328 Roo.data.ScriptTagProxy = function(config){
12329 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12330 Roo.apply(this, config);
12331 this.head = document.getElementsByTagName("head")[0];
12334 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12336 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12338 * @cfg {String} url The URL from which to request the data object.
12341 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12345 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12346 * the server the name of the callback function set up by the load call to process the returned data object.
12347 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12348 * javascript output which calls this named function passing the data object as its only parameter.
12350 callbackParam : "callback",
12352 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12353 * name to the request.
12358 * Load data from the configured URL, read the data object into
12359 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12360 * process that block using the passed callback.
12361 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12362 * for the request to the remote server.
12363 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12364 * object into a block of Roo.data.Records.
12365 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12366 * The function must be passed <ul>
12367 * <li>The Record block object</li>
12368 * <li>The "arg" argument from the load function</li>
12369 * <li>A boolean success indicator</li>
12371 * @param {Object} scope The scope in which to call the callback
12372 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12374 load : function(params, reader, callback, scope, arg){
12375 if(this.fireEvent("beforeload", this, params) !== false){
12377 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12379 var url = this.url;
12380 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12382 url += "&_dc=" + (new Date().getTime());
12384 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12387 cb : "stcCallback"+transId,
12388 scriptId : "stcScript"+transId,
12392 callback : callback,
12398 window[trans.cb] = function(o){
12399 conn.handleResponse(o, trans);
12402 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12404 if(this.autoAbort !== false){
12408 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12410 var script = document.createElement("script");
12411 script.setAttribute("src", url);
12412 script.setAttribute("type", "text/javascript");
12413 script.setAttribute("id", trans.scriptId);
12414 this.head.appendChild(script);
12416 this.trans = trans;
12418 callback.call(scope||this, null, arg, false);
12423 isLoading : function(){
12424 return this.trans ? true : false;
12428 * Abort the current server request.
12430 abort : function(){
12431 if(this.isLoading()){
12432 this.destroyTrans(this.trans);
12437 destroyTrans : function(trans, isLoaded){
12438 this.head.removeChild(document.getElementById(trans.scriptId));
12439 clearTimeout(trans.timeoutId);
12441 window[trans.cb] = undefined;
12443 delete window[trans.cb];
12446 // if hasn't been loaded, wait for load to remove it to prevent script error
12447 window[trans.cb] = function(){
12448 window[trans.cb] = undefined;
12450 delete window[trans.cb];
12457 handleResponse : function(o, trans){
12458 this.trans = false;
12459 this.destroyTrans(trans, true);
12462 result = trans.reader.readRecords(o);
12464 this.fireEvent("loadexception", this, o, trans.arg, e);
12465 trans.callback.call(trans.scope||window, null, trans.arg, false);
12468 this.fireEvent("load", this, o, trans.arg);
12469 trans.callback.call(trans.scope||window, result, trans.arg, true);
12473 handleFailure : function(trans){
12474 this.trans = false;
12475 this.destroyTrans(trans, false);
12476 this.fireEvent("loadexception", this, null, trans.arg);
12477 trans.callback.call(trans.scope||window, null, trans.arg, false);
12481 * Ext JS Library 1.1.1
12482 * Copyright(c) 2006-2007, Ext JS, LLC.
12484 * Originally Released Under LGPL - original licence link has changed is not relivant.
12487 * <script type="text/javascript">
12491 * @class Roo.data.JsonReader
12492 * @extends Roo.data.DataReader
12493 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12494 * based on mappings in a provided Roo.data.Record constructor.
12496 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12497 * in the reply previously.
12502 var RecordDef = Roo.data.Record.create([
12503 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12504 {name: 'occupation'} // This field will use "occupation" as the mapping.
12506 var myReader = new Roo.data.JsonReader({
12507 totalProperty: "results", // The property which contains the total dataset size (optional)
12508 root: "rows", // The property which contains an Array of row objects
12509 id: "id" // The property within each row object that provides an ID for the record (optional)
12513 * This would consume a JSON file like this:
12515 { 'results': 2, 'rows': [
12516 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12517 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12520 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12521 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12522 * paged from the remote server.
12523 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12524 * @cfg {String} root name of the property which contains the Array of row objects.
12525 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12526 * @cfg {Array} fields Array of field definition objects
12528 * Create a new JsonReader
12529 * @param {Object} meta Metadata configuration options
12530 * @param {Object} recordType Either an Array of field definition objects,
12531 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12533 Roo.data.JsonReader = function(meta, recordType){
12536 // set some defaults:
12537 Roo.applyIf(meta, {
12538 totalProperty: 'total',
12539 successProperty : 'success',
12544 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12546 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12549 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12550 * Used by Store query builder to append _requestMeta to params.
12553 metaFromRemote : false,
12555 * This method is only used by a DataProxy which has retrieved data from a remote server.
12556 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12557 * @return {Object} data A data block which is used by an Roo.data.Store object as
12558 * a cache of Roo.data.Records.
12560 read : function(response){
12561 var json = response.responseText;
12563 var o = /* eval:var:o */ eval("("+json+")");
12565 throw {message: "JsonReader.read: Json object not found"};
12571 this.metaFromRemote = true;
12572 this.meta = o.metaData;
12573 this.recordType = Roo.data.Record.create(o.metaData.fields);
12574 this.onMetaChange(this.meta, this.recordType, o);
12576 return this.readRecords(o);
12579 // private function a store will implement
12580 onMetaChange : function(meta, recordType, o){
12587 simpleAccess: function(obj, subsc) {
12594 getJsonAccessor: function(){
12596 return function(expr) {
12598 return(re.test(expr))
12599 ? new Function("obj", "return obj." + expr)
12604 return Roo.emptyFn;
12609 * Create a data block containing Roo.data.Records from an XML document.
12610 * @param {Object} o An object which contains an Array of row objects in the property specified
12611 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12612 * which contains the total size of the dataset.
12613 * @return {Object} data A data block which is used by an Roo.data.Store object as
12614 * a cache of Roo.data.Records.
12616 readRecords : function(o){
12618 * After any data loads, the raw JSON data is available for further custom processing.
12622 var s = this.meta, Record = this.recordType,
12623 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12625 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12627 if(s.totalProperty) {
12628 this.getTotal = this.getJsonAccessor(s.totalProperty);
12630 if(s.successProperty) {
12631 this.getSuccess = this.getJsonAccessor(s.successProperty);
12633 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12635 var g = this.getJsonAccessor(s.id);
12636 this.getId = function(rec) {
12638 return (r === undefined || r === "") ? null : r;
12641 this.getId = function(){return null;};
12644 for(var jj = 0; jj < fl; jj++){
12646 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12647 this.ef[jj] = this.getJsonAccessor(map);
12651 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12652 if(s.totalProperty){
12653 var vt = parseInt(this.getTotal(o), 10);
12658 if(s.successProperty){
12659 var vs = this.getSuccess(o);
12660 if(vs === false || vs === 'false'){
12665 for(var i = 0; i < c; i++){
12668 var id = this.getId(n);
12669 for(var j = 0; j < fl; j++){
12671 var v = this.ef[j](n);
12673 Roo.log('missing convert for ' + f.name);
12677 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12679 var record = new Record(values, id);
12681 records[i] = record;
12687 totalRecords : totalRecords
12692 * Ext JS Library 1.1.1
12693 * Copyright(c) 2006-2007, Ext JS, LLC.
12695 * Originally Released Under LGPL - original licence link has changed is not relivant.
12698 * <script type="text/javascript">
12702 * @class Roo.data.ArrayReader
12703 * @extends Roo.data.DataReader
12704 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12705 * Each element of that Array represents a row of data fields. The
12706 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12707 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12711 var RecordDef = Roo.data.Record.create([
12712 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12713 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12715 var myReader = new Roo.data.ArrayReader({
12716 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12720 * This would consume an Array like this:
12722 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12724 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12726 * Create a new JsonReader
12727 * @param {Object} meta Metadata configuration options.
12728 * @param {Object} recordType Either an Array of field definition objects
12729 * as specified to {@link Roo.data.Record#create},
12730 * or an {@link Roo.data.Record} object
12731 * created using {@link Roo.data.Record#create}.
12733 Roo.data.ArrayReader = function(meta, recordType){
12734 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12737 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12739 * Create a data block containing Roo.data.Records from an XML document.
12740 * @param {Object} o An Array of row objects which represents the dataset.
12741 * @return {Object} data A data block which is used by an Roo.data.Store object as
12742 * a cache of Roo.data.Records.
12744 readRecords : function(o){
12745 var sid = this.meta ? this.meta.id : null;
12746 var recordType = this.recordType, fields = recordType.prototype.fields;
12749 for(var i = 0; i < root.length; i++){
12752 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12753 for(var j = 0, jlen = fields.length; j < jlen; j++){
12754 var f = fields.items[j];
12755 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12756 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12758 values[f.name] = v;
12760 var record = new recordType(values, id);
12762 records[records.length] = record;
12766 totalRecords : records.length
12775 * @class Roo.bootstrap.ComboBox
12776 * @extends Roo.bootstrap.TriggerField
12777 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12778 * @cfg {Boolean} append (true|false) default false
12779 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12780 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12781 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12782 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12783 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12784 * @cfg {Boolean} animate default true
12785 * @cfg {Boolean} emptyResultText only for touch device
12786 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12787 * @cfg {String} emptyTitle default ''
12789 * Create a new ComboBox.
12790 * @param {Object} config Configuration options
12792 Roo.bootstrap.ComboBox = function(config){
12793 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12797 * Fires when the dropdown list is expanded
12798 * @param {Roo.bootstrap.ComboBox} combo This combo box
12803 * Fires when the dropdown list is collapsed
12804 * @param {Roo.bootstrap.ComboBox} combo This combo box
12808 * @event beforeselect
12809 * Fires before a list item is selected. Return false to cancel the selection.
12810 * @param {Roo.bootstrap.ComboBox} combo This combo box
12811 * @param {Roo.data.Record} record The data record returned from the underlying store
12812 * @param {Number} index The index of the selected item in the dropdown list
12814 'beforeselect' : true,
12817 * Fires when a list item is selected
12818 * @param {Roo.bootstrap.ComboBox} combo This combo box
12819 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12820 * @param {Number} index The index of the selected item in the dropdown list
12824 * @event beforequery
12825 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12826 * The event object passed has these properties:
12827 * @param {Roo.bootstrap.ComboBox} combo This combo box
12828 * @param {String} query The query
12829 * @param {Boolean} forceAll true to force "all" query
12830 * @param {Boolean} cancel true to cancel the query
12831 * @param {Object} e The query event object
12833 'beforequery': true,
12836 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12837 * @param {Roo.bootstrap.ComboBox} combo This combo box
12842 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12843 * @param {Roo.bootstrap.ComboBox} combo This combo box
12844 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12849 * Fires when the remove value from the combobox array
12850 * @param {Roo.bootstrap.ComboBox} combo This combo box
12854 * @event afterremove
12855 * Fires when the remove value from the combobox array
12856 * @param {Roo.bootstrap.ComboBox} combo This combo box
12858 'afterremove' : true,
12860 * @event specialfilter
12861 * Fires when specialfilter
12862 * @param {Roo.bootstrap.ComboBox} combo This combo box
12864 'specialfilter' : true,
12867 * Fires when tick the element
12868 * @param {Roo.bootstrap.ComboBox} combo This combo box
12872 * @event touchviewdisplay
12873 * Fires when touch view require special display (default is using displayField)
12874 * @param {Roo.bootstrap.ComboBox} combo This combo box
12875 * @param {Object} cfg set html .
12877 'touchviewdisplay' : true
12882 this.tickItems = [];
12884 this.selectedIndex = -1;
12885 if(this.mode == 'local'){
12886 if(config.queryDelay === undefined){
12887 this.queryDelay = 10;
12889 if(config.minChars === undefined){
12895 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12898 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12899 * rendering into an Roo.Editor, defaults to false)
12902 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12903 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12906 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12909 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12910 * the dropdown list (defaults to undefined, with no header element)
12914 * @cfg {String/Roo.Template} tpl The template to use to render the output
12918 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12920 listWidth: undefined,
12922 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12923 * mode = 'remote' or 'text' if mode = 'local')
12925 displayField: undefined,
12928 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12929 * mode = 'remote' or 'value' if mode = 'local').
12930 * Note: use of a valueField requires the user make a selection
12931 * in order for a value to be mapped.
12933 valueField: undefined,
12935 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12940 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12941 * field's data value (defaults to the underlying DOM element's name)
12943 hiddenName: undefined,
12945 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12949 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12951 selectedClass: 'active',
12954 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12958 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12959 * anchor positions (defaults to 'tl-bl')
12961 listAlign: 'tl-bl?',
12963 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12967 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12968 * query specified by the allQuery config option (defaults to 'query')
12970 triggerAction: 'query',
12972 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12973 * (defaults to 4, does not apply if editable = false)
12977 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12978 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12982 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12983 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12987 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12988 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12992 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12993 * when editable = true (defaults to false)
12995 selectOnFocus:false,
12997 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12999 queryParam: 'query',
13001 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13002 * when mode = 'remote' (defaults to 'Loading...')
13004 loadingText: 'Loading...',
13006 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13010 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13014 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13015 * traditional select (defaults to true)
13019 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13023 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13027 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13028 * listWidth has a higher value)
13032 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13033 * allow the user to set arbitrary text into the field (defaults to false)
13035 forceSelection:false,
13037 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13038 * if typeAhead = true (defaults to 250)
13040 typeAheadDelay : 250,
13042 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13043 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13045 valueNotFoundText : undefined,
13047 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13049 blockFocus : false,
13052 * @cfg {Boolean} disableClear Disable showing of clear button.
13054 disableClear : false,
13056 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13058 alwaysQuery : false,
13061 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13066 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13068 invalidClass : "has-warning",
13071 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13073 validClass : "has-success",
13076 * @cfg {Boolean} specialFilter (true|false) special filter default false
13078 specialFilter : false,
13081 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13083 mobileTouchView : true,
13086 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13088 useNativeIOS : false,
13091 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13093 mobile_restrict_height : false,
13095 ios_options : false,
13107 btnPosition : 'right',
13108 triggerList : true,
13109 showToggleBtn : true,
13111 emptyResultText: 'Empty',
13112 triggerText : 'Select',
13115 // element that contains real text value.. (when hidden is used..)
13117 getAutoCreate : function()
13122 * Render classic select for iso
13125 if(Roo.isIOS && this.useNativeIOS){
13126 cfg = this.getAutoCreateNativeIOS();
13134 if(Roo.isTouch && this.mobileTouchView){
13135 cfg = this.getAutoCreateTouchView();
13142 if(!this.tickable){
13143 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13148 * ComboBox with tickable selections
13151 var align = this.labelAlign || this.parentLabelAlign();
13154 cls : 'form-group roo-combobox-tickable' //input-group
13157 var btn_text_select = '';
13158 var btn_text_done = '';
13159 var btn_text_cancel = '';
13161 if (this.btn_text_show) {
13162 btn_text_select = 'Select';
13163 btn_text_done = 'Done';
13164 btn_text_cancel = 'Cancel';
13169 cls : 'tickable-buttons',
13174 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13175 //html : this.triggerText
13176 html: btn_text_select
13182 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13184 html: btn_text_done
13190 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13192 html: btn_text_cancel
13198 buttons.cn.unshift({
13200 cls: 'roo-select2-search-field-input'
13206 Roo.each(buttons.cn, function(c){
13208 c.cls += ' btn-' + _this.size;
13211 if (_this.disabled) {
13222 cls: 'form-hidden-field'
13226 cls: 'roo-select2-choices',
13230 cls: 'roo-select2-search-field',
13241 cls: 'roo-select2-container input-group roo-select2-container-multi',
13246 // cls: 'typeahead typeahead-long dropdown-menu',
13247 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13252 if(this.hasFeedback && !this.allowBlank){
13256 cls: 'glyphicon form-control-feedback'
13259 combobox.cn.push(feedback);
13263 if (align ==='left' && this.fieldLabel.length) {
13265 cfg.cls += ' roo-form-group-label-left';
13270 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13271 tooltip : 'This field is required'
13276 cls : 'control-label',
13277 html : this.fieldLabel
13289 var labelCfg = cfg.cn[1];
13290 var contentCfg = cfg.cn[2];
13293 if(this.indicatorpos == 'right'){
13299 cls : 'control-label',
13303 html : this.fieldLabel
13307 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13308 tooltip : 'This field is required'
13323 labelCfg = cfg.cn[0];
13324 contentCfg = cfg.cn[1];
13328 if(this.labelWidth > 12){
13329 labelCfg.style = "width: " + this.labelWidth + 'px';
13332 if(this.labelWidth < 13 && this.labelmd == 0){
13333 this.labelmd = this.labelWidth;
13336 if(this.labellg > 0){
13337 labelCfg.cls += ' col-lg-' + this.labellg;
13338 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13341 if(this.labelmd > 0){
13342 labelCfg.cls += ' col-md-' + this.labelmd;
13343 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13346 if(this.labelsm > 0){
13347 labelCfg.cls += ' col-sm-' + this.labelsm;
13348 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13351 if(this.labelxs > 0){
13352 labelCfg.cls += ' col-xs-' + this.labelxs;
13353 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13357 } else if ( this.fieldLabel.length) {
13358 // Roo.log(" label");
13362 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13363 tooltip : 'This field is required'
13367 //cls : 'input-group-addon',
13368 html : this.fieldLabel
13373 if(this.indicatorpos == 'right'){
13377 //cls : 'input-group-addon',
13378 html : this.fieldLabel
13382 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13383 tooltip : 'This field is required'
13392 // Roo.log(" no label && no align");
13399 ['xs','sm','md','lg'].map(function(size){
13400 if (settings[size]) {
13401 cfg.cls += ' col-' + size + '-' + settings[size];
13409 _initEventsCalled : false,
13412 initEvents: function()
13414 if (this._initEventsCalled) { // as we call render... prevent looping...
13417 this._initEventsCalled = true;
13420 throw "can not find store for combo";
13423 this.indicator = this.indicatorEl();
13425 this.store = Roo.factory(this.store, Roo.data);
13426 this.store.parent = this;
13428 // if we are building from html. then this element is so complex, that we can not really
13429 // use the rendered HTML.
13430 // so we have to trash and replace the previous code.
13431 if (Roo.XComponent.build_from_html) {
13432 // remove this element....
13433 var e = this.el.dom, k=0;
13434 while (e ) { e = e.previousSibling; ++k;}
13439 this.rendered = false;
13441 this.render(this.parent().getChildContainer(true), k);
13444 if(Roo.isIOS && this.useNativeIOS){
13445 this.initIOSView();
13453 if(Roo.isTouch && this.mobileTouchView){
13454 this.initTouchView();
13459 this.initTickableEvents();
13463 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13465 if(this.hiddenName){
13467 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13469 this.hiddenField.dom.value =
13470 this.hiddenValue !== undefined ? this.hiddenValue :
13471 this.value !== undefined ? this.value : '';
13473 // prevent input submission
13474 this.el.dom.removeAttribute('name');
13475 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13480 // this.el.dom.setAttribute('autocomplete', 'off');
13483 var cls = 'x-combo-list';
13485 //this.list = new Roo.Layer({
13486 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13492 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13493 _this.list.setWidth(lw);
13496 this.list.on('mouseover', this.onViewOver, this);
13497 this.list.on('mousemove', this.onViewMove, this);
13498 this.list.on('scroll', this.onViewScroll, this);
13501 this.list.swallowEvent('mousewheel');
13502 this.assetHeight = 0;
13505 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13506 this.assetHeight += this.header.getHeight();
13509 this.innerList = this.list.createChild({cls:cls+'-inner'});
13510 this.innerList.on('mouseover', this.onViewOver, this);
13511 this.innerList.on('mousemove', this.onViewMove, this);
13512 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13514 if(this.allowBlank && !this.pageSize && !this.disableClear){
13515 this.footer = this.list.createChild({cls:cls+'-ft'});
13516 this.pageTb = new Roo.Toolbar(this.footer);
13520 this.footer = this.list.createChild({cls:cls+'-ft'});
13521 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13522 {pageSize: this.pageSize});
13526 if (this.pageTb && this.allowBlank && !this.disableClear) {
13528 this.pageTb.add(new Roo.Toolbar.Fill(), {
13529 cls: 'x-btn-icon x-btn-clear',
13531 handler: function()
13534 _this.clearValue();
13535 _this.onSelect(false, -1);
13540 this.assetHeight += this.footer.getHeight();
13545 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13548 this.view = new Roo.View(this.list, this.tpl, {
13549 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13551 //this.view.wrapEl.setDisplayed(false);
13552 this.view.on('click', this.onViewClick, this);
13555 this.store.on('beforeload', this.onBeforeLoad, this);
13556 this.store.on('load', this.onLoad, this);
13557 this.store.on('loadexception', this.onLoadException, this);
13559 if(this.resizable){
13560 this.resizer = new Roo.Resizable(this.list, {
13561 pinned:true, handles:'se'
13563 this.resizer.on('resize', function(r, w, h){
13564 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13565 this.listWidth = w;
13566 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13567 this.restrictHeight();
13569 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13572 if(!this.editable){
13573 this.editable = true;
13574 this.setEditable(false);
13579 if (typeof(this.events.add.listeners) != 'undefined') {
13581 this.addicon = this.wrap.createChild(
13582 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13584 this.addicon.on('click', function(e) {
13585 this.fireEvent('add', this);
13588 if (typeof(this.events.edit.listeners) != 'undefined') {
13590 this.editicon = this.wrap.createChild(
13591 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13592 if (this.addicon) {
13593 this.editicon.setStyle('margin-left', '40px');
13595 this.editicon.on('click', function(e) {
13597 // we fire even if inothing is selected..
13598 this.fireEvent('edit', this, this.lastData );
13604 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13605 "up" : function(e){
13606 this.inKeyMode = true;
13610 "down" : function(e){
13611 if(!this.isExpanded()){
13612 this.onTriggerClick();
13614 this.inKeyMode = true;
13619 "enter" : function(e){
13620 // this.onViewClick();
13624 if(this.fireEvent("specialkey", this, e)){
13625 this.onViewClick(false);
13631 "esc" : function(e){
13635 "tab" : function(e){
13638 if(this.fireEvent("specialkey", this, e)){
13639 this.onViewClick(false);
13647 doRelay : function(foo, bar, hname){
13648 if(hname == 'down' || this.scope.isExpanded()){
13649 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13658 this.queryDelay = Math.max(this.queryDelay || 10,
13659 this.mode == 'local' ? 10 : 250);
13662 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13664 if(this.typeAhead){
13665 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13667 if(this.editable !== false){
13668 this.inputEl().on("keyup", this.onKeyUp, this);
13670 if(this.forceSelection){
13671 this.inputEl().on('blur', this.doForce, this);
13675 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13676 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13680 initTickableEvents: function()
13684 if(this.hiddenName){
13686 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13688 this.hiddenField.dom.value =
13689 this.hiddenValue !== undefined ? this.hiddenValue :
13690 this.value !== undefined ? this.value : '';
13692 // prevent input submission
13693 this.el.dom.removeAttribute('name');
13694 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13699 // this.list = this.el.select('ul.dropdown-menu',true).first();
13701 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13702 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13703 if(this.triggerList){
13704 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13707 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13708 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13710 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13711 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13713 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13714 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13716 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13717 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13718 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13721 this.cancelBtn.hide();
13726 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13727 _this.list.setWidth(lw);
13730 this.list.on('mouseover', this.onViewOver, this);
13731 this.list.on('mousemove', this.onViewMove, this);
13733 this.list.on('scroll', this.onViewScroll, this);
13736 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13737 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13740 this.view = new Roo.View(this.list, this.tpl, {
13745 selectedClass: this.selectedClass
13748 //this.view.wrapEl.setDisplayed(false);
13749 this.view.on('click', this.onViewClick, this);
13753 this.store.on('beforeload', this.onBeforeLoad, this);
13754 this.store.on('load', this.onLoad, this);
13755 this.store.on('loadexception', this.onLoadException, this);
13758 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13759 "up" : function(e){
13760 this.inKeyMode = true;
13764 "down" : function(e){
13765 this.inKeyMode = true;
13769 "enter" : function(e){
13770 if(this.fireEvent("specialkey", this, e)){
13771 this.onViewClick(false);
13777 "esc" : function(e){
13778 this.onTickableFooterButtonClick(e, false, false);
13781 "tab" : function(e){
13782 this.fireEvent("specialkey", this, e);
13784 this.onTickableFooterButtonClick(e, false, false);
13791 doRelay : function(e, fn, key){
13792 if(this.scope.isExpanded()){
13793 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13802 this.queryDelay = Math.max(this.queryDelay || 10,
13803 this.mode == 'local' ? 10 : 250);
13806 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13808 if(this.typeAhead){
13809 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13812 if(this.editable !== false){
13813 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13816 this.indicator = this.indicatorEl();
13818 if(this.indicator){
13819 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13820 this.indicator.hide();
13825 onDestroy : function(){
13827 this.view.setStore(null);
13828 this.view.el.removeAllListeners();
13829 this.view.el.remove();
13830 this.view.purgeListeners();
13833 this.list.dom.innerHTML = '';
13837 this.store.un('beforeload', this.onBeforeLoad, this);
13838 this.store.un('load', this.onLoad, this);
13839 this.store.un('loadexception', this.onLoadException, this);
13841 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13845 fireKey : function(e){
13846 if(e.isNavKeyPress() && !this.list.isVisible()){
13847 this.fireEvent("specialkey", this, e);
13852 onResize: function(w, h){
13853 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13855 // if(typeof w != 'number'){
13856 // // we do not handle it!?!?
13859 // var tw = this.trigger.getWidth();
13860 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13861 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13863 // this.inputEl().setWidth( this.adjustWidth('input', x));
13865 // //this.trigger.setStyle('left', x+'px');
13867 // if(this.list && this.listWidth === undefined){
13868 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13869 // this.list.setWidth(lw);
13870 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13878 * Allow or prevent the user from directly editing the field text. If false is passed,
13879 * the user will only be able to select from the items defined in the dropdown list. This method
13880 * is the runtime equivalent of setting the 'editable' config option at config time.
13881 * @param {Boolean} value True to allow the user to directly edit the field text
13883 setEditable : function(value){
13884 if(value == this.editable){
13887 this.editable = value;
13889 this.inputEl().dom.setAttribute('readOnly', true);
13890 this.inputEl().on('mousedown', this.onTriggerClick, this);
13891 this.inputEl().addClass('x-combo-noedit');
13893 this.inputEl().dom.setAttribute('readOnly', false);
13894 this.inputEl().un('mousedown', this.onTriggerClick, this);
13895 this.inputEl().removeClass('x-combo-noedit');
13901 onBeforeLoad : function(combo,opts){
13902 if(!this.hasFocus){
13906 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13908 this.restrictHeight();
13909 this.selectedIndex = -1;
13913 onLoad : function(){
13915 this.hasQuery = false;
13917 if(!this.hasFocus){
13921 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13922 this.loading.hide();
13925 if(this.store.getCount() > 0){
13928 this.restrictHeight();
13929 if(this.lastQuery == this.allQuery){
13930 if(this.editable && !this.tickable){
13931 this.inputEl().dom.select();
13935 !this.selectByValue(this.value, true) &&
13938 !this.store.lastOptions ||
13939 typeof(this.store.lastOptions.add) == 'undefined' ||
13940 this.store.lastOptions.add != true
13943 this.select(0, true);
13946 if(this.autoFocus){
13949 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13950 this.taTask.delay(this.typeAheadDelay);
13954 this.onEmptyResults();
13960 onLoadException : function()
13962 this.hasQuery = false;
13964 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13965 this.loading.hide();
13968 if(this.tickable && this.editable){
13973 // only causes errors at present
13974 //Roo.log(this.store.reader.jsonData);
13975 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13977 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13983 onTypeAhead : function(){
13984 if(this.store.getCount() > 0){
13985 var r = this.store.getAt(0);
13986 var newValue = r.data[this.displayField];
13987 var len = newValue.length;
13988 var selStart = this.getRawValue().length;
13990 if(selStart != len){
13991 this.setRawValue(newValue);
13992 this.selectText(selStart, newValue.length);
13998 onSelect : function(record, index){
14000 if(this.fireEvent('beforeselect', this, record, index) !== false){
14002 this.setFromData(index > -1 ? record.data : false);
14005 this.fireEvent('select', this, record, index);
14010 * Returns the currently selected field value or empty string if no value is set.
14011 * @return {String} value The selected value
14013 getValue : function()
14015 if(Roo.isIOS && this.useNativeIOS){
14016 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14020 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14023 if(this.valueField){
14024 return typeof this.value != 'undefined' ? this.value : '';
14026 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14030 getRawValue : function()
14032 if(Roo.isIOS && this.useNativeIOS){
14033 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14036 var v = this.inputEl().getValue();
14042 * Clears any text/value currently set in the field
14044 clearValue : function(){
14046 if(this.hiddenField){
14047 this.hiddenField.dom.value = '';
14050 this.setRawValue('');
14051 this.lastSelectionText = '';
14052 this.lastData = false;
14054 var close = this.closeTriggerEl();
14065 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14066 * will be displayed in the field. If the value does not match the data value of an existing item,
14067 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14068 * Otherwise the field will be blank (although the value will still be set).
14069 * @param {String} value The value to match
14071 setValue : function(v)
14073 if(Roo.isIOS && this.useNativeIOS){
14074 this.setIOSValue(v);
14084 if(this.valueField){
14085 var r = this.findRecord(this.valueField, v);
14087 text = r.data[this.displayField];
14088 }else if(this.valueNotFoundText !== undefined){
14089 text = this.valueNotFoundText;
14092 this.lastSelectionText = text;
14093 if(this.hiddenField){
14094 this.hiddenField.dom.value = v;
14096 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14099 var close = this.closeTriggerEl();
14102 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14108 * @property {Object} the last set data for the element
14113 * Sets the value of the field based on a object which is related to the record format for the store.
14114 * @param {Object} value the value to set as. or false on reset?
14116 setFromData : function(o){
14123 var dv = ''; // display value
14124 var vv = ''; // value value..
14126 if (this.displayField) {
14127 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14129 // this is an error condition!!!
14130 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14133 if(this.valueField){
14134 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14137 var close = this.closeTriggerEl();
14140 if(dv.length || vv * 1 > 0){
14142 this.blockFocus=true;
14148 if(this.hiddenField){
14149 this.hiddenField.dom.value = vv;
14151 this.lastSelectionText = dv;
14152 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14156 // no hidden field.. - we store the value in 'value', but still display
14157 // display field!!!!
14158 this.lastSelectionText = dv;
14159 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14166 reset : function(){
14167 // overridden so that last data is reset..
14174 this.setValue(this.originalValue);
14175 //this.clearInvalid();
14176 this.lastData = false;
14178 this.view.clearSelections();
14184 findRecord : function(prop, value){
14186 if(this.store.getCount() > 0){
14187 this.store.each(function(r){
14188 if(r.data[prop] == value){
14198 getName: function()
14200 // returns hidden if it's set..
14201 if (!this.rendered) {return ''};
14202 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14206 onViewMove : function(e, t){
14207 this.inKeyMode = false;
14211 onViewOver : function(e, t){
14212 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14215 var item = this.view.findItemFromChild(t);
14218 var index = this.view.indexOf(item);
14219 this.select(index, false);
14224 onViewClick : function(view, doFocus, el, e)
14226 var index = this.view.getSelectedIndexes()[0];
14228 var r = this.store.getAt(index);
14232 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14239 Roo.each(this.tickItems, function(v,k){
14241 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14243 _this.tickItems.splice(k, 1);
14245 if(typeof(e) == 'undefined' && view == false){
14246 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14258 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14259 this.tickItems.push(r.data);
14262 if(typeof(e) == 'undefined' && view == false){
14263 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14270 this.onSelect(r, index);
14272 if(doFocus !== false && !this.blockFocus){
14273 this.inputEl().focus();
14278 restrictHeight : function(){
14279 //this.innerList.dom.style.height = '';
14280 //var inner = this.innerList.dom;
14281 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14282 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14283 //this.list.beginUpdate();
14284 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14285 this.list.alignTo(this.inputEl(), this.listAlign);
14286 this.list.alignTo(this.inputEl(), this.listAlign);
14287 //this.list.endUpdate();
14291 onEmptyResults : function(){
14293 if(this.tickable && this.editable){
14294 this.hasFocus = false;
14295 this.restrictHeight();
14303 * Returns true if the dropdown list is expanded, else false.
14305 isExpanded : function(){
14306 return this.list.isVisible();
14310 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14311 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14312 * @param {String} value The data value of the item to select
14313 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14314 * selected item if it is not currently in view (defaults to true)
14315 * @return {Boolean} True if the value matched an item in the list, else false
14317 selectByValue : function(v, scrollIntoView){
14318 if(v !== undefined && v !== null){
14319 var r = this.findRecord(this.valueField || this.displayField, v);
14321 this.select(this.store.indexOf(r), scrollIntoView);
14329 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14330 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14331 * @param {Number} index The zero-based index of the list item to select
14332 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14333 * selected item if it is not currently in view (defaults to true)
14335 select : function(index, scrollIntoView){
14336 this.selectedIndex = index;
14337 this.view.select(index);
14338 if(scrollIntoView !== false){
14339 var el = this.view.getNode(index);
14341 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14344 this.list.scrollChildIntoView(el, false);
14350 selectNext : function(){
14351 var ct = this.store.getCount();
14353 if(this.selectedIndex == -1){
14355 }else if(this.selectedIndex < ct-1){
14356 this.select(this.selectedIndex+1);
14362 selectPrev : function(){
14363 var ct = this.store.getCount();
14365 if(this.selectedIndex == -1){
14367 }else if(this.selectedIndex != 0){
14368 this.select(this.selectedIndex-1);
14374 onKeyUp : function(e){
14375 if(this.editable !== false && !e.isSpecialKey()){
14376 this.lastKey = e.getKey();
14377 this.dqTask.delay(this.queryDelay);
14382 validateBlur : function(){
14383 return !this.list || !this.list.isVisible();
14387 initQuery : function(){
14389 var v = this.getRawValue();
14391 if(this.tickable && this.editable){
14392 v = this.tickableInputEl().getValue();
14399 doForce : function(){
14400 if(this.inputEl().dom.value.length > 0){
14401 this.inputEl().dom.value =
14402 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14408 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14409 * query allowing the query action to be canceled if needed.
14410 * @param {String} query The SQL query to execute
14411 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14412 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14413 * saved in the current store (defaults to false)
14415 doQuery : function(q, forceAll){
14417 if(q === undefined || q === null){
14422 forceAll: forceAll,
14426 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14431 forceAll = qe.forceAll;
14432 if(forceAll === true || (q.length >= this.minChars)){
14434 this.hasQuery = true;
14436 if(this.lastQuery != q || this.alwaysQuery){
14437 this.lastQuery = q;
14438 if(this.mode == 'local'){
14439 this.selectedIndex = -1;
14441 this.store.clearFilter();
14444 if(this.specialFilter){
14445 this.fireEvent('specialfilter', this);
14450 this.store.filter(this.displayField, q);
14453 this.store.fireEvent("datachanged", this.store);
14460 this.store.baseParams[this.queryParam] = q;
14462 var options = {params : this.getParams(q)};
14465 options.add = true;
14466 options.params.start = this.page * this.pageSize;
14469 this.store.load(options);
14472 * this code will make the page width larger, at the beginning, the list not align correctly,
14473 * we should expand the list on onLoad
14474 * so command out it
14479 this.selectedIndex = -1;
14484 this.loadNext = false;
14488 getParams : function(q){
14490 //p[this.queryParam] = q;
14494 p.limit = this.pageSize;
14500 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14502 collapse : function(){
14503 if(!this.isExpanded()){
14509 this.hasFocus = false;
14513 this.cancelBtn.hide();
14514 this.trigger.show();
14517 this.tickableInputEl().dom.value = '';
14518 this.tickableInputEl().blur();
14523 Roo.get(document).un('mousedown', this.collapseIf, this);
14524 Roo.get(document).un('mousewheel', this.collapseIf, this);
14525 if (!this.editable) {
14526 Roo.get(document).un('keydown', this.listKeyPress, this);
14528 this.fireEvent('collapse', this);
14534 collapseIf : function(e){
14535 var in_combo = e.within(this.el);
14536 var in_list = e.within(this.list);
14537 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14539 if (in_combo || in_list || is_list) {
14540 //e.stopPropagation();
14545 this.onTickableFooterButtonClick(e, false, false);
14553 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14555 expand : function(){
14557 if(this.isExpanded() || !this.hasFocus){
14561 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14562 this.list.setWidth(lw);
14568 this.restrictHeight();
14572 this.tickItems = Roo.apply([], this.item);
14575 this.cancelBtn.show();
14576 this.trigger.hide();
14579 this.tickableInputEl().focus();
14584 Roo.get(document).on('mousedown', this.collapseIf, this);
14585 Roo.get(document).on('mousewheel', this.collapseIf, this);
14586 if (!this.editable) {
14587 Roo.get(document).on('keydown', this.listKeyPress, this);
14590 this.fireEvent('expand', this);
14594 // Implements the default empty TriggerField.onTriggerClick function
14595 onTriggerClick : function(e)
14597 Roo.log('trigger click');
14599 if(this.disabled || !this.triggerList){
14604 this.loadNext = false;
14606 if(this.isExpanded()){
14608 if (!this.blockFocus) {
14609 this.inputEl().focus();
14613 this.hasFocus = true;
14614 if(this.triggerAction == 'all') {
14615 this.doQuery(this.allQuery, true);
14617 this.doQuery(this.getRawValue());
14619 if (!this.blockFocus) {
14620 this.inputEl().focus();
14625 onTickableTriggerClick : function(e)
14632 this.loadNext = false;
14633 this.hasFocus = true;
14635 if(this.triggerAction == 'all') {
14636 this.doQuery(this.allQuery, true);
14638 this.doQuery(this.getRawValue());
14642 onSearchFieldClick : function(e)
14644 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14645 this.onTickableFooterButtonClick(e, false, false);
14649 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14654 this.loadNext = false;
14655 this.hasFocus = true;
14657 if(this.triggerAction == 'all') {
14658 this.doQuery(this.allQuery, true);
14660 this.doQuery(this.getRawValue());
14664 listKeyPress : function(e)
14666 //Roo.log('listkeypress');
14667 // scroll to first matching element based on key pres..
14668 if (e.isSpecialKey()) {
14671 var k = String.fromCharCode(e.getKey()).toUpperCase();
14674 var csel = this.view.getSelectedNodes();
14675 var cselitem = false;
14677 var ix = this.view.indexOf(csel[0]);
14678 cselitem = this.store.getAt(ix);
14679 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14685 this.store.each(function(v) {
14687 // start at existing selection.
14688 if (cselitem.id == v.id) {
14694 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14695 match = this.store.indexOf(v);
14701 if (match === false) {
14702 return true; // no more action?
14705 this.view.select(match);
14706 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14707 sn.scrollIntoView(sn.dom.parentNode, false);
14710 onViewScroll : function(e, t){
14712 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){
14716 this.hasQuery = true;
14718 this.loading = this.list.select('.loading', true).first();
14720 if(this.loading === null){
14721 this.list.createChild({
14723 cls: 'loading roo-select2-more-results roo-select2-active',
14724 html: 'Loading more results...'
14727 this.loading = this.list.select('.loading', true).first();
14729 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14731 this.loading.hide();
14734 this.loading.show();
14739 this.loadNext = true;
14741 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14746 addItem : function(o)
14748 var dv = ''; // display value
14750 if (this.displayField) {
14751 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14753 // this is an error condition!!!
14754 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14761 var choice = this.choices.createChild({
14763 cls: 'roo-select2-search-choice',
14772 cls: 'roo-select2-search-choice-close fa fa-times',
14777 }, this.searchField);
14779 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14781 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14789 this.inputEl().dom.value = '';
14794 onRemoveItem : function(e, _self, o)
14796 e.preventDefault();
14798 this.lastItem = Roo.apply([], this.item);
14800 var index = this.item.indexOf(o.data) * 1;
14803 Roo.log('not this item?!');
14807 this.item.splice(index, 1);
14812 this.fireEvent('remove', this, e);
14818 syncValue : function()
14820 if(!this.item.length){
14827 Roo.each(this.item, function(i){
14828 if(_this.valueField){
14829 value.push(i[_this.valueField]);
14836 this.value = value.join(',');
14838 if(this.hiddenField){
14839 this.hiddenField.dom.value = this.value;
14842 this.store.fireEvent("datachanged", this.store);
14847 clearItem : function()
14849 if(!this.multiple){
14855 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14863 if(this.tickable && !Roo.isTouch){
14864 this.view.refresh();
14868 inputEl: function ()
14870 if(Roo.isIOS && this.useNativeIOS){
14871 return this.el.select('select.roo-ios-select', true).first();
14874 if(Roo.isTouch && this.mobileTouchView){
14875 return this.el.select('input.form-control',true).first();
14879 return this.searchField;
14882 return this.el.select('input.form-control',true).first();
14885 onTickableFooterButtonClick : function(e, btn, el)
14887 e.preventDefault();
14889 this.lastItem = Roo.apply([], this.item);
14891 if(btn && btn.name == 'cancel'){
14892 this.tickItems = Roo.apply([], this.item);
14901 Roo.each(this.tickItems, function(o){
14909 validate : function()
14911 if(this.getVisibilityEl().hasClass('hidden')){
14915 var v = this.getRawValue();
14918 v = this.getValue();
14921 if(this.disabled || this.allowBlank || v.length){
14926 this.markInvalid();
14930 tickableInputEl : function()
14932 if(!this.tickable || !this.editable){
14933 return this.inputEl();
14936 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14940 getAutoCreateTouchView : function()
14945 cls: 'form-group' //input-group
14951 type : this.inputType,
14952 cls : 'form-control x-combo-noedit',
14953 autocomplete: 'new-password',
14954 placeholder : this.placeholder || '',
14959 input.name = this.name;
14963 input.cls += ' input-' + this.size;
14966 if (this.disabled) {
14967 input.disabled = true;
14978 inputblock.cls += ' input-group';
14980 inputblock.cn.unshift({
14982 cls : 'input-group-addon',
14987 if(this.removable && !this.multiple){
14988 inputblock.cls += ' roo-removable';
14990 inputblock.cn.push({
14993 cls : 'roo-combo-removable-btn close'
14997 if(this.hasFeedback && !this.allowBlank){
14999 inputblock.cls += ' has-feedback';
15001 inputblock.cn.push({
15003 cls: 'glyphicon form-control-feedback'
15010 inputblock.cls += (this.before) ? '' : ' input-group';
15012 inputblock.cn.push({
15014 cls : 'input-group-addon',
15025 cls: 'form-hidden-field'
15039 cls: 'form-hidden-field'
15043 cls: 'roo-select2-choices',
15047 cls: 'roo-select2-search-field',
15060 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15066 if(!this.multiple && this.showToggleBtn){
15073 if (this.caret != false) {
15076 cls: 'fa fa-' + this.caret
15083 cls : 'input-group-addon btn dropdown-toggle',
15088 cls: 'combobox-clear',
15102 combobox.cls += ' roo-select2-container-multi';
15105 var align = this.labelAlign || this.parentLabelAlign();
15107 if (align ==='left' && this.fieldLabel.length) {
15112 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15113 tooltip : 'This field is required'
15117 cls : 'control-label',
15118 html : this.fieldLabel
15129 var labelCfg = cfg.cn[1];
15130 var contentCfg = cfg.cn[2];
15133 if(this.indicatorpos == 'right'){
15138 cls : 'control-label',
15142 html : this.fieldLabel
15146 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15147 tooltip : 'This field is required'
15160 labelCfg = cfg.cn[0];
15161 contentCfg = cfg.cn[1];
15166 if(this.labelWidth > 12){
15167 labelCfg.style = "width: " + this.labelWidth + 'px';
15170 if(this.labelWidth < 13 && this.labelmd == 0){
15171 this.labelmd = this.labelWidth;
15174 if(this.labellg > 0){
15175 labelCfg.cls += ' col-lg-' + this.labellg;
15176 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15179 if(this.labelmd > 0){
15180 labelCfg.cls += ' col-md-' + this.labelmd;
15181 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15184 if(this.labelsm > 0){
15185 labelCfg.cls += ' col-sm-' + this.labelsm;
15186 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15189 if(this.labelxs > 0){
15190 labelCfg.cls += ' col-xs-' + this.labelxs;
15191 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15195 } else if ( this.fieldLabel.length) {
15199 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15200 tooltip : 'This field is required'
15204 cls : 'control-label',
15205 html : this.fieldLabel
15216 if(this.indicatorpos == 'right'){
15220 cls : 'control-label',
15221 html : this.fieldLabel,
15225 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15226 tooltip : 'This field is required'
15243 var settings = this;
15245 ['xs','sm','md','lg'].map(function(size){
15246 if (settings[size]) {
15247 cfg.cls += ' col-' + size + '-' + settings[size];
15254 initTouchView : function()
15256 this.renderTouchView();
15258 this.touchViewEl.on('scroll', function(){
15259 this.el.dom.scrollTop = 0;
15262 this.originalValue = this.getValue();
15264 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15266 this.inputEl().on("click", this.showTouchView, this);
15267 if (this.triggerEl) {
15268 this.triggerEl.on("click", this.showTouchView, this);
15272 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15273 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15275 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15277 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15278 this.store.on('load', this.onTouchViewLoad, this);
15279 this.store.on('loadexception', this.onTouchViewLoadException, this);
15281 if(this.hiddenName){
15283 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15285 this.hiddenField.dom.value =
15286 this.hiddenValue !== undefined ? this.hiddenValue :
15287 this.value !== undefined ? this.value : '';
15289 this.el.dom.removeAttribute('name');
15290 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15294 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15295 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15298 if(this.removable && !this.multiple){
15299 var close = this.closeTriggerEl();
15301 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15302 close.on('click', this.removeBtnClick, this, close);
15306 * fix the bug in Safari iOS8
15308 this.inputEl().on("focus", function(e){
15309 document.activeElement.blur();
15312 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15319 renderTouchView : function()
15321 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15322 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15324 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15325 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15327 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15328 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15329 this.touchViewBodyEl.setStyle('overflow', 'auto');
15331 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15332 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15334 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15335 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15339 showTouchView : function()
15345 this.touchViewHeaderEl.hide();
15347 if(this.modalTitle.length){
15348 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15349 this.touchViewHeaderEl.show();
15352 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15353 this.touchViewEl.show();
15355 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15357 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15358 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15360 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15362 if(this.modalTitle.length){
15363 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15366 this.touchViewBodyEl.setHeight(bodyHeight);
15370 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15372 this.touchViewEl.addClass('in');
15375 if(this._touchViewMask){
15376 Roo.get(document.body).addClass("x-body-masked");
15377 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15378 this._touchViewMask.setStyle('z-index', 10000);
15379 this._touchViewMask.addClass('show');
15382 this.doTouchViewQuery();
15386 hideTouchView : function()
15388 this.touchViewEl.removeClass('in');
15392 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15394 this.touchViewEl.setStyle('display', 'none');
15397 if(this._touchViewMask){
15398 this._touchViewMask.removeClass('show');
15399 Roo.get(document.body).removeClass("x-body-masked");
15403 setTouchViewValue : function()
15410 Roo.each(this.tickItems, function(o){
15415 this.hideTouchView();
15418 doTouchViewQuery : function()
15427 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15431 if(!this.alwaysQuery || this.mode == 'local'){
15432 this.onTouchViewLoad();
15439 onTouchViewBeforeLoad : function(combo,opts)
15445 onTouchViewLoad : function()
15447 if(this.store.getCount() < 1){
15448 this.onTouchViewEmptyResults();
15452 this.clearTouchView();
15454 var rawValue = this.getRawValue();
15456 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15458 this.tickItems = [];
15460 this.store.data.each(function(d, rowIndex){
15461 var row = this.touchViewListGroup.createChild(template);
15463 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15464 row.addClass(d.data.cls);
15467 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15470 html : d.data[this.displayField]
15473 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15474 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15477 row.removeClass('selected');
15478 if(!this.multiple && this.valueField &&
15479 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15482 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15483 row.addClass('selected');
15486 if(this.multiple && this.valueField &&
15487 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15491 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15492 this.tickItems.push(d.data);
15495 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15499 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15501 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15503 if(this.modalTitle.length){
15504 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15507 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15509 if(this.mobile_restrict_height && listHeight < bodyHeight){
15510 this.touchViewBodyEl.setHeight(listHeight);
15515 if(firstChecked && listHeight > bodyHeight){
15516 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15521 onTouchViewLoadException : function()
15523 this.hideTouchView();
15526 onTouchViewEmptyResults : function()
15528 this.clearTouchView();
15530 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15532 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15536 clearTouchView : function()
15538 this.touchViewListGroup.dom.innerHTML = '';
15541 onTouchViewClick : function(e, el, o)
15543 e.preventDefault();
15546 var rowIndex = o.rowIndex;
15548 var r = this.store.getAt(rowIndex);
15550 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15552 if(!this.multiple){
15553 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15554 c.dom.removeAttribute('checked');
15557 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15559 this.setFromData(r.data);
15561 var close = this.closeTriggerEl();
15567 this.hideTouchView();
15569 this.fireEvent('select', this, r, rowIndex);
15574 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15575 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15576 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15580 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15581 this.addItem(r.data);
15582 this.tickItems.push(r.data);
15586 getAutoCreateNativeIOS : function()
15589 cls: 'form-group' //input-group,
15594 cls : 'roo-ios-select'
15598 combobox.name = this.name;
15601 if (this.disabled) {
15602 combobox.disabled = true;
15605 var settings = this;
15607 ['xs','sm','md','lg'].map(function(size){
15608 if (settings[size]) {
15609 cfg.cls += ' col-' + size + '-' + settings[size];
15619 initIOSView : function()
15621 this.store.on('load', this.onIOSViewLoad, this);
15626 onIOSViewLoad : function()
15628 if(this.store.getCount() < 1){
15632 this.clearIOSView();
15634 if(this.allowBlank) {
15636 var default_text = '-- SELECT --';
15638 if(this.placeholder.length){
15639 default_text = this.placeholder;
15642 if(this.emptyTitle.length){
15643 default_text += ' - ' + this.emptyTitle + ' -';
15646 var opt = this.inputEl().createChild({
15649 html : default_text
15653 o[this.valueField] = 0;
15654 o[this.displayField] = default_text;
15656 this.ios_options.push({
15663 this.store.data.each(function(d, rowIndex){
15667 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15668 html = d.data[this.displayField];
15673 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15674 value = d.data[this.valueField];
15683 if(this.value == d.data[this.valueField]){
15684 option['selected'] = true;
15687 var opt = this.inputEl().createChild(option);
15689 this.ios_options.push({
15696 this.inputEl().on('change', function(){
15697 this.fireEvent('select', this);
15702 clearIOSView: function()
15704 this.inputEl().dom.innerHTML = '';
15706 this.ios_options = [];
15709 setIOSValue: function(v)
15713 if(!this.ios_options){
15717 Roo.each(this.ios_options, function(opts){
15719 opts.el.dom.removeAttribute('selected');
15721 if(opts.data[this.valueField] != v){
15725 opts.el.dom.setAttribute('selected', true);
15731 * @cfg {Boolean} grow
15735 * @cfg {Number} growMin
15739 * @cfg {Number} growMax
15748 Roo.apply(Roo.bootstrap.ComboBox, {
15752 cls: 'modal-header',
15774 cls: 'list-group-item',
15778 cls: 'roo-combobox-list-group-item-value'
15782 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15796 listItemCheckbox : {
15798 cls: 'list-group-item',
15802 cls: 'roo-combobox-list-group-item-value'
15806 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15822 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15827 cls: 'modal-footer',
15835 cls: 'col-xs-6 text-left',
15838 cls: 'btn btn-danger roo-touch-view-cancel',
15844 cls: 'col-xs-6 text-right',
15847 cls: 'btn btn-success roo-touch-view-ok',
15858 Roo.apply(Roo.bootstrap.ComboBox, {
15860 touchViewTemplate : {
15862 cls: 'modal fade roo-combobox-touch-view',
15866 cls: 'modal-dialog',
15867 style : 'position:fixed', // we have to fix position....
15871 cls: 'modal-content',
15873 Roo.bootstrap.ComboBox.header,
15874 Roo.bootstrap.ComboBox.body,
15875 Roo.bootstrap.ComboBox.footer
15884 * Ext JS Library 1.1.1
15885 * Copyright(c) 2006-2007, Ext JS, LLC.
15887 * Originally Released Under LGPL - original licence link has changed is not relivant.
15890 * <script type="text/javascript">
15895 * @extends Roo.util.Observable
15896 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15897 * This class also supports single and multi selection modes. <br>
15898 * Create a data model bound view:
15900 var store = new Roo.data.Store(...);
15902 var view = new Roo.View({
15904 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15906 singleSelect: true,
15907 selectedClass: "ydataview-selected",
15911 // listen for node click?
15912 view.on("click", function(vw, index, node, e){
15913 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15917 dataModel.load("foobar.xml");
15919 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15921 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15922 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15924 * Note: old style constructor is still suported (container, template, config)
15927 * Create a new View
15928 * @param {Object} config The config object
15931 Roo.View = function(config, depreciated_tpl, depreciated_config){
15933 this.parent = false;
15935 if (typeof(depreciated_tpl) == 'undefined') {
15936 // new way.. - universal constructor.
15937 Roo.apply(this, config);
15938 this.el = Roo.get(this.el);
15941 this.el = Roo.get(config);
15942 this.tpl = depreciated_tpl;
15943 Roo.apply(this, depreciated_config);
15945 this.wrapEl = this.el.wrap().wrap();
15946 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15949 if(typeof(this.tpl) == "string"){
15950 this.tpl = new Roo.Template(this.tpl);
15952 // support xtype ctors..
15953 this.tpl = new Roo.factory(this.tpl, Roo);
15957 this.tpl.compile();
15962 * @event beforeclick
15963 * Fires before a click is processed. Returns false to cancel the default action.
15964 * @param {Roo.View} this
15965 * @param {Number} index The index of the target node
15966 * @param {HTMLElement} node The target node
15967 * @param {Roo.EventObject} e The raw event object
15969 "beforeclick" : true,
15972 * Fires when a template node is clicked.
15973 * @param {Roo.View} this
15974 * @param {Number} index The index of the target node
15975 * @param {HTMLElement} node The target node
15976 * @param {Roo.EventObject} e The raw event object
15981 * Fires when a template node is double clicked.
15982 * @param {Roo.View} this
15983 * @param {Number} index The index of the target node
15984 * @param {HTMLElement} node The target node
15985 * @param {Roo.EventObject} e The raw event object
15989 * @event contextmenu
15990 * Fires when a template node is right clicked.
15991 * @param {Roo.View} this
15992 * @param {Number} index The index of the target node
15993 * @param {HTMLElement} node The target node
15994 * @param {Roo.EventObject} e The raw event object
15996 "contextmenu" : true,
15998 * @event selectionchange
15999 * Fires when the selected nodes change.
16000 * @param {Roo.View} this
16001 * @param {Array} selections Array of the selected nodes
16003 "selectionchange" : true,
16006 * @event beforeselect
16007 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16008 * @param {Roo.View} this
16009 * @param {HTMLElement} node The node to be selected
16010 * @param {Array} selections Array of currently selected nodes
16012 "beforeselect" : true,
16014 * @event preparedata
16015 * Fires on every row to render, to allow you to change the data.
16016 * @param {Roo.View} this
16017 * @param {Object} data to be rendered (change this)
16019 "preparedata" : true
16027 "click": this.onClick,
16028 "dblclick": this.onDblClick,
16029 "contextmenu": this.onContextMenu,
16033 this.selections = [];
16035 this.cmp = new Roo.CompositeElementLite([]);
16037 this.store = Roo.factory(this.store, Roo.data);
16038 this.setStore(this.store, true);
16041 if ( this.footer && this.footer.xtype) {
16043 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16045 this.footer.dataSource = this.store;
16046 this.footer.container = fctr;
16047 this.footer = Roo.factory(this.footer, Roo);
16048 fctr.insertFirst(this.el);
16050 // this is a bit insane - as the paging toolbar seems to detach the el..
16051 // dom.parentNode.parentNode.parentNode
16052 // they get detached?
16056 Roo.View.superclass.constructor.call(this);
16061 Roo.extend(Roo.View, Roo.util.Observable, {
16064 * @cfg {Roo.data.Store} store Data store to load data from.
16069 * @cfg {String|Roo.Element} el The container element.
16074 * @cfg {String|Roo.Template} tpl The template used by this View
16078 * @cfg {String} dataName the named area of the template to use as the data area
16079 * Works with domtemplates roo-name="name"
16083 * @cfg {String} selectedClass The css class to add to selected nodes
16085 selectedClass : "x-view-selected",
16087 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16092 * @cfg {String} text to display on mask (default Loading)
16096 * @cfg {Boolean} multiSelect Allow multiple selection
16098 multiSelect : false,
16100 * @cfg {Boolean} singleSelect Allow single selection
16102 singleSelect: false,
16105 * @cfg {Boolean} toggleSelect - selecting
16107 toggleSelect : false,
16110 * @cfg {Boolean} tickable - selecting
16115 * Returns the element this view is bound to.
16116 * @return {Roo.Element}
16118 getEl : function(){
16119 return this.wrapEl;
16125 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16127 refresh : function(){
16128 //Roo.log('refresh');
16131 // if we are using something like 'domtemplate', then
16132 // the what gets used is:
16133 // t.applySubtemplate(NAME, data, wrapping data..)
16134 // the outer template then get' applied with
16135 // the store 'extra data'
16136 // and the body get's added to the
16137 // roo-name="data" node?
16138 // <span class='roo-tpl-{name}'></span> ?????
16142 this.clearSelections();
16143 this.el.update("");
16145 var records = this.store.getRange();
16146 if(records.length < 1) {
16148 // is this valid?? = should it render a template??
16150 this.el.update(this.emptyText);
16154 if (this.dataName) {
16155 this.el.update(t.apply(this.store.meta)); //????
16156 el = this.el.child('.roo-tpl-' + this.dataName);
16159 for(var i = 0, len = records.length; i < len; i++){
16160 var data = this.prepareData(records[i].data, i, records[i]);
16161 this.fireEvent("preparedata", this, data, i, records[i]);
16163 var d = Roo.apply({}, data);
16166 Roo.apply(d, {'roo-id' : Roo.id()});
16170 Roo.each(this.parent.item, function(item){
16171 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16174 Roo.apply(d, {'roo-data-checked' : 'checked'});
16178 html[html.length] = Roo.util.Format.trim(
16180 t.applySubtemplate(this.dataName, d, this.store.meta) :
16187 el.update(html.join(""));
16188 this.nodes = el.dom.childNodes;
16189 this.updateIndexes(0);
16194 * Function to override to reformat the data that is sent to
16195 * the template for each node.
16196 * DEPRICATED - use the preparedata event handler.
16197 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16198 * a JSON object for an UpdateManager bound view).
16200 prepareData : function(data, index, record)
16202 this.fireEvent("preparedata", this, data, index, record);
16206 onUpdate : function(ds, record){
16207 // Roo.log('on update');
16208 this.clearSelections();
16209 var index = this.store.indexOf(record);
16210 var n = this.nodes[index];
16211 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16212 n.parentNode.removeChild(n);
16213 this.updateIndexes(index, index);
16219 onAdd : function(ds, records, index)
16221 //Roo.log(['on Add', ds, records, index] );
16222 this.clearSelections();
16223 if(this.nodes.length == 0){
16227 var n = this.nodes[index];
16228 for(var i = 0, len = records.length; i < len; i++){
16229 var d = this.prepareData(records[i].data, i, records[i]);
16231 this.tpl.insertBefore(n, d);
16234 this.tpl.append(this.el, d);
16237 this.updateIndexes(index);
16240 onRemove : function(ds, record, index){
16241 // Roo.log('onRemove');
16242 this.clearSelections();
16243 var el = this.dataName ?
16244 this.el.child('.roo-tpl-' + this.dataName) :
16247 el.dom.removeChild(this.nodes[index]);
16248 this.updateIndexes(index);
16252 * Refresh an individual node.
16253 * @param {Number} index
16255 refreshNode : function(index){
16256 this.onUpdate(this.store, this.store.getAt(index));
16259 updateIndexes : function(startIndex, endIndex){
16260 var ns = this.nodes;
16261 startIndex = startIndex || 0;
16262 endIndex = endIndex || ns.length - 1;
16263 for(var i = startIndex; i <= endIndex; i++){
16264 ns[i].nodeIndex = i;
16269 * Changes the data store this view uses and refresh the view.
16270 * @param {Store} store
16272 setStore : function(store, initial){
16273 if(!initial && this.store){
16274 this.store.un("datachanged", this.refresh);
16275 this.store.un("add", this.onAdd);
16276 this.store.un("remove", this.onRemove);
16277 this.store.un("update", this.onUpdate);
16278 this.store.un("clear", this.refresh);
16279 this.store.un("beforeload", this.onBeforeLoad);
16280 this.store.un("load", this.onLoad);
16281 this.store.un("loadexception", this.onLoad);
16285 store.on("datachanged", this.refresh, this);
16286 store.on("add", this.onAdd, this);
16287 store.on("remove", this.onRemove, this);
16288 store.on("update", this.onUpdate, this);
16289 store.on("clear", this.refresh, this);
16290 store.on("beforeload", this.onBeforeLoad, this);
16291 store.on("load", this.onLoad, this);
16292 store.on("loadexception", this.onLoad, this);
16300 * onbeforeLoad - masks the loading area.
16303 onBeforeLoad : function(store,opts)
16305 //Roo.log('onBeforeLoad');
16307 this.el.update("");
16309 this.el.mask(this.mask ? this.mask : "Loading" );
16311 onLoad : function ()
16318 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16319 * @param {HTMLElement} node
16320 * @return {HTMLElement} The template node
16322 findItemFromChild : function(node){
16323 var el = this.dataName ?
16324 this.el.child('.roo-tpl-' + this.dataName,true) :
16327 if(!node || node.parentNode == el){
16330 var p = node.parentNode;
16331 while(p && p != el){
16332 if(p.parentNode == el){
16341 onClick : function(e){
16342 var item = this.findItemFromChild(e.getTarget());
16344 var index = this.indexOf(item);
16345 if(this.onItemClick(item, index, e) !== false){
16346 this.fireEvent("click", this, index, item, e);
16349 this.clearSelections();
16354 onContextMenu : function(e){
16355 var item = this.findItemFromChild(e.getTarget());
16357 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16362 onDblClick : function(e){
16363 var item = this.findItemFromChild(e.getTarget());
16365 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16369 onItemClick : function(item, index, e)
16371 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16374 if (this.toggleSelect) {
16375 var m = this.isSelected(item) ? 'unselect' : 'select';
16378 _t[m](item, true, false);
16381 if(this.multiSelect || this.singleSelect){
16382 if(this.multiSelect && e.shiftKey && this.lastSelection){
16383 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16385 this.select(item, this.multiSelect && e.ctrlKey);
16386 this.lastSelection = item;
16389 if(!this.tickable){
16390 e.preventDefault();
16398 * Get the number of selected nodes.
16401 getSelectionCount : function(){
16402 return this.selections.length;
16406 * Get the currently selected nodes.
16407 * @return {Array} An array of HTMLElements
16409 getSelectedNodes : function(){
16410 return this.selections;
16414 * Get the indexes of the selected nodes.
16417 getSelectedIndexes : function(){
16418 var indexes = [], s = this.selections;
16419 for(var i = 0, len = s.length; i < len; i++){
16420 indexes.push(s[i].nodeIndex);
16426 * Clear all selections
16427 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16429 clearSelections : function(suppressEvent){
16430 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16431 this.cmp.elements = this.selections;
16432 this.cmp.removeClass(this.selectedClass);
16433 this.selections = [];
16434 if(!suppressEvent){
16435 this.fireEvent("selectionchange", this, this.selections);
16441 * Returns true if the passed node is selected
16442 * @param {HTMLElement/Number} node The node or node index
16443 * @return {Boolean}
16445 isSelected : function(node){
16446 var s = this.selections;
16450 node = this.getNode(node);
16451 return s.indexOf(node) !== -1;
16456 * @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
16457 * @param {Boolean} keepExisting (optional) true to keep existing selections
16458 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16460 select : function(nodeInfo, keepExisting, suppressEvent){
16461 if(nodeInfo instanceof Array){
16463 this.clearSelections(true);
16465 for(var i = 0, len = nodeInfo.length; i < len; i++){
16466 this.select(nodeInfo[i], true, true);
16470 var node = this.getNode(nodeInfo);
16471 if(!node || this.isSelected(node)){
16472 return; // already selected.
16475 this.clearSelections(true);
16478 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16479 Roo.fly(node).addClass(this.selectedClass);
16480 this.selections.push(node);
16481 if(!suppressEvent){
16482 this.fireEvent("selectionchange", this, this.selections);
16490 * @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
16491 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16492 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16494 unselect : function(nodeInfo, keepExisting, suppressEvent)
16496 if(nodeInfo instanceof Array){
16497 Roo.each(this.selections, function(s) {
16498 this.unselect(s, nodeInfo);
16502 var node = this.getNode(nodeInfo);
16503 if(!node || !this.isSelected(node)){
16504 //Roo.log("not selected");
16505 return; // not selected.
16509 Roo.each(this.selections, function(s) {
16511 Roo.fly(node).removeClass(this.selectedClass);
16518 this.selections= ns;
16519 this.fireEvent("selectionchange", this, this.selections);
16523 * Gets a template node.
16524 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16525 * @return {HTMLElement} The node or null if it wasn't found
16527 getNode : function(nodeInfo){
16528 if(typeof nodeInfo == "string"){
16529 return document.getElementById(nodeInfo);
16530 }else if(typeof nodeInfo == "number"){
16531 return this.nodes[nodeInfo];
16537 * Gets a range template nodes.
16538 * @param {Number} startIndex
16539 * @param {Number} endIndex
16540 * @return {Array} An array of nodes
16542 getNodes : function(start, end){
16543 var ns = this.nodes;
16544 start = start || 0;
16545 end = typeof end == "undefined" ? ns.length - 1 : end;
16548 for(var i = start; i <= end; i++){
16552 for(var i = start; i >= end; i--){
16560 * Finds the index of the passed node
16561 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16562 * @return {Number} The index of the node or -1
16564 indexOf : function(node){
16565 node = this.getNode(node);
16566 if(typeof node.nodeIndex == "number"){
16567 return node.nodeIndex;
16569 var ns = this.nodes;
16570 for(var i = 0, len = ns.length; i < len; i++){
16581 * based on jquery fullcalendar
16585 Roo.bootstrap = Roo.bootstrap || {};
16587 * @class Roo.bootstrap.Calendar
16588 * @extends Roo.bootstrap.Component
16589 * Bootstrap Calendar class
16590 * @cfg {Boolean} loadMask (true|false) default false
16591 * @cfg {Object} header generate the user specific header of the calendar, default false
16594 * Create a new Container
16595 * @param {Object} config The config object
16600 Roo.bootstrap.Calendar = function(config){
16601 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16605 * Fires when a date is selected
16606 * @param {DatePicker} this
16607 * @param {Date} date The selected date
16611 * @event monthchange
16612 * Fires when the displayed month changes
16613 * @param {DatePicker} this
16614 * @param {Date} date The selected month
16616 'monthchange': true,
16618 * @event evententer
16619 * Fires when mouse over an event
16620 * @param {Calendar} this
16621 * @param {event} Event
16623 'evententer': true,
16625 * @event eventleave
16626 * Fires when the mouse leaves an
16627 * @param {Calendar} this
16630 'eventleave': true,
16632 * @event eventclick
16633 * Fires when the mouse click an
16634 * @param {Calendar} this
16643 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16646 * @cfg {Number} startDay
16647 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16655 getAutoCreate : function(){
16658 var fc_button = function(name, corner, style, content ) {
16659 return Roo.apply({},{
16661 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16663 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16666 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16677 style : 'width:100%',
16684 cls : 'fc-header-left',
16686 fc_button('prev', 'left', 'arrow', '‹' ),
16687 fc_button('next', 'right', 'arrow', '›' ),
16688 { tag: 'span', cls: 'fc-header-space' },
16689 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16697 cls : 'fc-header-center',
16701 cls: 'fc-header-title',
16704 html : 'month / year'
16712 cls : 'fc-header-right',
16714 /* fc_button('month', 'left', '', 'month' ),
16715 fc_button('week', '', '', 'week' ),
16716 fc_button('day', 'right', '', 'day' )
16728 header = this.header;
16731 var cal_heads = function() {
16733 // fixme - handle this.
16735 for (var i =0; i < Date.dayNames.length; i++) {
16736 var d = Date.dayNames[i];
16739 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16740 html : d.substring(0,3)
16744 ret[0].cls += ' fc-first';
16745 ret[6].cls += ' fc-last';
16748 var cal_cell = function(n) {
16751 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16756 cls: 'fc-day-number',
16760 cls: 'fc-day-content',
16764 style: 'position: relative;' // height: 17px;
16776 var cal_rows = function() {
16779 for (var r = 0; r < 6; r++) {
16786 for (var i =0; i < Date.dayNames.length; i++) {
16787 var d = Date.dayNames[i];
16788 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16791 row.cn[0].cls+=' fc-first';
16792 row.cn[0].cn[0].style = 'min-height:90px';
16793 row.cn[6].cls+=' fc-last';
16797 ret[0].cls += ' fc-first';
16798 ret[4].cls += ' fc-prev-last';
16799 ret[5].cls += ' fc-last';
16806 cls: 'fc-border-separate',
16807 style : 'width:100%',
16815 cls : 'fc-first fc-last',
16833 cls : 'fc-content',
16834 style : "position: relative;",
16837 cls : 'fc-view fc-view-month fc-grid',
16838 style : 'position: relative',
16839 unselectable : 'on',
16842 cls : 'fc-event-container',
16843 style : 'position:absolute;z-index:8;top:0;left:0;'
16861 initEvents : function()
16864 throw "can not find store for calendar";
16870 style: "text-align:center",
16874 style: "background-color:white;width:50%;margin:250 auto",
16878 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16889 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16891 var size = this.el.select('.fc-content', true).first().getSize();
16892 this.maskEl.setSize(size.width, size.height);
16893 this.maskEl.enableDisplayMode("block");
16894 if(!this.loadMask){
16895 this.maskEl.hide();
16898 this.store = Roo.factory(this.store, Roo.data);
16899 this.store.on('load', this.onLoad, this);
16900 this.store.on('beforeload', this.onBeforeLoad, this);
16904 this.cells = this.el.select('.fc-day',true);
16905 //Roo.log(this.cells);
16906 this.textNodes = this.el.query('.fc-day-number');
16907 this.cells.addClassOnOver('fc-state-hover');
16909 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16910 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16911 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16912 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16914 this.on('monthchange', this.onMonthChange, this);
16916 this.update(new Date().clearTime());
16919 resize : function() {
16920 var sz = this.el.getSize();
16922 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16923 this.el.select('.fc-day-content div',true).setHeight(34);
16928 showPrevMonth : function(e){
16929 this.update(this.activeDate.add("mo", -1));
16931 showToday : function(e){
16932 this.update(new Date().clearTime());
16935 showNextMonth : function(e){
16936 this.update(this.activeDate.add("mo", 1));
16940 showPrevYear : function(){
16941 this.update(this.activeDate.add("y", -1));
16945 showNextYear : function(){
16946 this.update(this.activeDate.add("y", 1));
16951 update : function(date)
16953 var vd = this.activeDate;
16954 this.activeDate = date;
16955 // if(vd && this.el){
16956 // var t = date.getTime();
16957 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16958 // Roo.log('using add remove');
16960 // this.fireEvent('monthchange', this, date);
16962 // this.cells.removeClass("fc-state-highlight");
16963 // this.cells.each(function(c){
16964 // if(c.dateValue == t){
16965 // c.addClass("fc-state-highlight");
16966 // setTimeout(function(){
16967 // try{c.dom.firstChild.focus();}catch(e){}
16977 var days = date.getDaysInMonth();
16979 var firstOfMonth = date.getFirstDateOfMonth();
16980 var startingPos = firstOfMonth.getDay()-this.startDay;
16982 if(startingPos < this.startDay){
16986 var pm = date.add(Date.MONTH, -1);
16987 var prevStart = pm.getDaysInMonth()-startingPos;
16989 this.cells = this.el.select('.fc-day',true);
16990 this.textNodes = this.el.query('.fc-day-number');
16991 this.cells.addClassOnOver('fc-state-hover');
16993 var cells = this.cells.elements;
16994 var textEls = this.textNodes;
16996 Roo.each(cells, function(cell){
16997 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17000 days += startingPos;
17002 // convert everything to numbers so it's fast
17003 var day = 86400000;
17004 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17007 //Roo.log(prevStart);
17009 var today = new Date().clearTime().getTime();
17010 var sel = date.clearTime().getTime();
17011 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17012 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17013 var ddMatch = this.disabledDatesRE;
17014 var ddText = this.disabledDatesText;
17015 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17016 var ddaysText = this.disabledDaysText;
17017 var format = this.format;
17019 var setCellClass = function(cal, cell){
17023 //Roo.log('set Cell Class');
17025 var t = d.getTime();
17029 cell.dateValue = t;
17031 cell.className += " fc-today";
17032 cell.className += " fc-state-highlight";
17033 cell.title = cal.todayText;
17036 // disable highlight in other month..
17037 //cell.className += " fc-state-highlight";
17042 cell.className = " fc-state-disabled";
17043 cell.title = cal.minText;
17047 cell.className = " fc-state-disabled";
17048 cell.title = cal.maxText;
17052 if(ddays.indexOf(d.getDay()) != -1){
17053 cell.title = ddaysText;
17054 cell.className = " fc-state-disabled";
17057 if(ddMatch && format){
17058 var fvalue = d.dateFormat(format);
17059 if(ddMatch.test(fvalue)){
17060 cell.title = ddText.replace("%0", fvalue);
17061 cell.className = " fc-state-disabled";
17065 if (!cell.initialClassName) {
17066 cell.initialClassName = cell.dom.className;
17069 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17074 for(; i < startingPos; i++) {
17075 textEls[i].innerHTML = (++prevStart);
17076 d.setDate(d.getDate()+1);
17078 cells[i].className = "fc-past fc-other-month";
17079 setCellClass(this, cells[i]);
17084 for(; i < days; i++){
17085 intDay = i - startingPos + 1;
17086 textEls[i].innerHTML = (intDay);
17087 d.setDate(d.getDate()+1);
17089 cells[i].className = ''; // "x-date-active";
17090 setCellClass(this, cells[i]);
17094 for(; i < 42; i++) {
17095 textEls[i].innerHTML = (++extraDays);
17096 d.setDate(d.getDate()+1);
17098 cells[i].className = "fc-future fc-other-month";
17099 setCellClass(this, cells[i]);
17102 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17104 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17106 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17107 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17109 if(totalRows != 6){
17110 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17111 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17114 this.fireEvent('monthchange', this, date);
17118 if(!this.internalRender){
17119 var main = this.el.dom.firstChild;
17120 var w = main.offsetWidth;
17121 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17122 Roo.fly(main).setWidth(w);
17123 this.internalRender = true;
17124 // opera does not respect the auto grow header center column
17125 // then, after it gets a width opera refuses to recalculate
17126 // without a second pass
17127 if(Roo.isOpera && !this.secondPass){
17128 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17129 this.secondPass = true;
17130 this.update.defer(10, this, [date]);
17137 findCell : function(dt) {
17138 dt = dt.clearTime().getTime();
17140 this.cells.each(function(c){
17141 //Roo.log("check " +c.dateValue + '?=' + dt);
17142 if(c.dateValue == dt){
17152 findCells : function(ev) {
17153 var s = ev.start.clone().clearTime().getTime();
17155 var e= ev.end.clone().clearTime().getTime();
17158 this.cells.each(function(c){
17159 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17161 if(c.dateValue > e){
17164 if(c.dateValue < s){
17173 // findBestRow: function(cells)
17177 // for (var i =0 ; i < cells.length;i++) {
17178 // ret = Math.max(cells[i].rows || 0,ret);
17185 addItem : function(ev)
17187 // look for vertical location slot in
17188 var cells = this.findCells(ev);
17190 // ev.row = this.findBestRow(cells);
17192 // work out the location.
17196 for(var i =0; i < cells.length; i++) {
17198 cells[i].row = cells[0].row;
17201 cells[i].row = cells[i].row + 1;
17211 if (crow.start.getY() == cells[i].getY()) {
17213 crow.end = cells[i];
17230 cells[0].events.push(ev);
17232 this.calevents.push(ev);
17235 clearEvents: function() {
17237 if(!this.calevents){
17241 Roo.each(this.cells.elements, function(c){
17247 Roo.each(this.calevents, function(e) {
17248 Roo.each(e.els, function(el) {
17249 el.un('mouseenter' ,this.onEventEnter, this);
17250 el.un('mouseleave' ,this.onEventLeave, this);
17255 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17261 renderEvents: function()
17265 this.cells.each(function(c) {
17274 if(c.row != c.events.length){
17275 r = 4 - (4 - (c.row - c.events.length));
17278 c.events = ev.slice(0, r);
17279 c.more = ev.slice(r);
17281 if(c.more.length && c.more.length == 1){
17282 c.events.push(c.more.pop());
17285 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17289 this.cells.each(function(c) {
17291 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17294 for (var e = 0; e < c.events.length; e++){
17295 var ev = c.events[e];
17296 var rows = ev.rows;
17298 for(var i = 0; i < rows.length; i++) {
17300 // how many rows should it span..
17303 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17304 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17306 unselectable : "on",
17309 cls: 'fc-event-inner',
17313 // cls: 'fc-event-time',
17314 // html : cells.length > 1 ? '' : ev.time
17318 cls: 'fc-event-title',
17319 html : String.format('{0}', ev.title)
17326 cls: 'ui-resizable-handle ui-resizable-e',
17327 html : '  '
17334 cfg.cls += ' fc-event-start';
17336 if ((i+1) == rows.length) {
17337 cfg.cls += ' fc-event-end';
17340 var ctr = _this.el.select('.fc-event-container',true).first();
17341 var cg = ctr.createChild(cfg);
17343 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17344 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17346 var r = (c.more.length) ? 1 : 0;
17347 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17348 cg.setWidth(ebox.right - sbox.x -2);
17350 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17351 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17352 cg.on('click', _this.onEventClick, _this, ev);
17363 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17364 style : 'position: absolute',
17365 unselectable : "on",
17368 cls: 'fc-event-inner',
17372 cls: 'fc-event-title',
17380 cls: 'ui-resizable-handle ui-resizable-e',
17381 html : '  '
17387 var ctr = _this.el.select('.fc-event-container',true).first();
17388 var cg = ctr.createChild(cfg);
17390 var sbox = c.select('.fc-day-content',true).first().getBox();
17391 var ebox = c.select('.fc-day-content',true).first().getBox();
17393 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17394 cg.setWidth(ebox.right - sbox.x -2);
17396 cg.on('click', _this.onMoreEventClick, _this, c.more);
17406 onEventEnter: function (e, el,event,d) {
17407 this.fireEvent('evententer', this, el, event);
17410 onEventLeave: function (e, el,event,d) {
17411 this.fireEvent('eventleave', this, el, event);
17414 onEventClick: function (e, el,event,d) {
17415 this.fireEvent('eventclick', this, el, event);
17418 onMonthChange: function () {
17422 onMoreEventClick: function(e, el, more)
17426 this.calpopover.placement = 'right';
17427 this.calpopover.setTitle('More');
17429 this.calpopover.setContent('');
17431 var ctr = this.calpopover.el.select('.popover-content', true).first();
17433 Roo.each(more, function(m){
17435 cls : 'fc-event-hori fc-event-draggable',
17438 var cg = ctr.createChild(cfg);
17440 cg.on('click', _this.onEventClick, _this, m);
17443 this.calpopover.show(el);
17448 onLoad: function ()
17450 this.calevents = [];
17453 if(this.store.getCount() > 0){
17454 this.store.data.each(function(d){
17457 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17458 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17459 time : d.data.start_time,
17460 title : d.data.title,
17461 description : d.data.description,
17462 venue : d.data.venue
17467 this.renderEvents();
17469 if(this.calevents.length && this.loadMask){
17470 this.maskEl.hide();
17474 onBeforeLoad: function()
17476 this.clearEvents();
17478 this.maskEl.show();
17492 * @class Roo.bootstrap.Popover
17493 * @extends Roo.bootstrap.Component
17494 * Bootstrap Popover class
17495 * @cfg {String} html contents of the popover (or false to use children..)
17496 * @cfg {String} title of popover (or false to hide)
17497 * @cfg {String} placement how it is placed
17498 * @cfg {String} trigger click || hover (or false to trigger manually)
17499 * @cfg {String} over what (parent or false to trigger manually.)
17500 * @cfg {Number} delay - delay before showing
17503 * Create a new Popover
17504 * @param {Object} config The config object
17507 Roo.bootstrap.Popover = function(config){
17508 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17514 * After the popover show
17516 * @param {Roo.bootstrap.Popover} this
17521 * After the popover hide
17523 * @param {Roo.bootstrap.Popover} this
17529 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17531 title: 'Fill in a title',
17534 placement : 'right',
17535 trigger : 'hover', // hover
17541 can_build_overlaid : false,
17543 getChildContainer : function()
17545 return this.el.select('.popover-content',true).first();
17548 getAutoCreate : function(){
17551 cls : 'popover roo-dynamic',
17552 style: 'display:block',
17558 cls : 'popover-inner',
17562 cls: 'popover-title',
17566 cls : 'popover-content',
17577 setTitle: function(str)
17580 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17582 setContent: function(str)
17585 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17587 // as it get's added to the bottom of the page.
17588 onRender : function(ct, position)
17590 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17592 var cfg = Roo.apply({}, this.getAutoCreate());
17596 cfg.cls += ' ' + this.cls;
17599 cfg.style = this.style;
17601 //Roo.log("adding to ");
17602 this.el = Roo.get(document.body).createChild(cfg, position);
17603 // Roo.log(this.el);
17608 initEvents : function()
17610 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17611 this.el.enableDisplayMode('block');
17613 if (this.over === false) {
17616 if (this.triggers === false) {
17619 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17620 var triggers = this.trigger ? this.trigger.split(' ') : [];
17621 Roo.each(triggers, function(trigger) {
17623 if (trigger == 'click') {
17624 on_el.on('click', this.toggle, this);
17625 } else if (trigger != 'manual') {
17626 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17627 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17629 on_el.on(eventIn ,this.enter, this);
17630 on_el.on(eventOut, this.leave, this);
17641 toggle : function () {
17642 this.hoverState == 'in' ? this.leave() : this.enter();
17645 enter : function () {
17647 clearTimeout(this.timeout);
17649 this.hoverState = 'in';
17651 if (!this.delay || !this.delay.show) {
17656 this.timeout = setTimeout(function () {
17657 if (_t.hoverState == 'in') {
17660 }, this.delay.show)
17663 leave : function() {
17664 clearTimeout(this.timeout);
17666 this.hoverState = 'out';
17668 if (!this.delay || !this.delay.hide) {
17673 this.timeout = setTimeout(function () {
17674 if (_t.hoverState == 'out') {
17677 }, this.delay.hide)
17680 show : function (on_el)
17683 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17687 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17688 if (this.html !== false) {
17689 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17691 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17692 if (!this.title.length) {
17693 this.el.select('.popover-title',true).hide();
17696 var placement = typeof this.placement == 'function' ?
17697 this.placement.call(this, this.el, on_el) :
17700 var autoToken = /\s?auto?\s?/i;
17701 var autoPlace = autoToken.test(placement);
17703 placement = placement.replace(autoToken, '') || 'top';
17707 //this.el.setXY([0,0]);
17709 this.el.dom.style.display='block';
17710 this.el.addClass(placement);
17712 //this.el.appendTo(on_el);
17714 var p = this.getPosition();
17715 var box = this.el.getBox();
17720 var align = Roo.bootstrap.Popover.alignment[placement];
17723 this.el.alignTo(on_el, align[0],align[1]);
17724 //var arrow = this.el.select('.arrow',true).first();
17725 //arrow.set(align[2],
17727 this.el.addClass('in');
17730 if (this.el.hasClass('fade')) {
17734 this.hoverState = 'in';
17736 this.fireEvent('show', this);
17741 this.el.setXY([0,0]);
17742 this.el.removeClass('in');
17744 this.hoverState = null;
17746 this.fireEvent('hide', this);
17751 Roo.bootstrap.Popover.alignment = {
17752 'left' : ['r-l', [-10,0], 'right'],
17753 'right' : ['l-r', [10,0], 'left'],
17754 'bottom' : ['t-b', [0,10], 'top'],
17755 'top' : [ 'b-t', [0,-10], 'bottom']
17766 * @class Roo.bootstrap.Progress
17767 * @extends Roo.bootstrap.Component
17768 * Bootstrap Progress class
17769 * @cfg {Boolean} striped striped of the progress bar
17770 * @cfg {Boolean} active animated of the progress bar
17774 * Create a new Progress
17775 * @param {Object} config The config object
17778 Roo.bootstrap.Progress = function(config){
17779 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17782 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17787 getAutoCreate : function(){
17795 cfg.cls += ' progress-striped';
17799 cfg.cls += ' active';
17818 * @class Roo.bootstrap.ProgressBar
17819 * @extends Roo.bootstrap.Component
17820 * Bootstrap ProgressBar class
17821 * @cfg {Number} aria_valuenow aria-value now
17822 * @cfg {Number} aria_valuemin aria-value min
17823 * @cfg {Number} aria_valuemax aria-value max
17824 * @cfg {String} label label for the progress bar
17825 * @cfg {String} panel (success | info | warning | danger )
17826 * @cfg {String} role role of the progress bar
17827 * @cfg {String} sr_only text
17831 * Create a new ProgressBar
17832 * @param {Object} config The config object
17835 Roo.bootstrap.ProgressBar = function(config){
17836 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17839 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17843 aria_valuemax : 100,
17849 getAutoCreate : function()
17854 cls: 'progress-bar',
17855 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17867 cfg.role = this.role;
17870 if(this.aria_valuenow){
17871 cfg['aria-valuenow'] = this.aria_valuenow;
17874 if(this.aria_valuemin){
17875 cfg['aria-valuemin'] = this.aria_valuemin;
17878 if(this.aria_valuemax){
17879 cfg['aria-valuemax'] = this.aria_valuemax;
17882 if(this.label && !this.sr_only){
17883 cfg.html = this.label;
17887 cfg.cls += ' progress-bar-' + this.panel;
17893 update : function(aria_valuenow)
17895 this.aria_valuenow = aria_valuenow;
17897 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17912 * @class Roo.bootstrap.TabGroup
17913 * @extends Roo.bootstrap.Column
17914 * Bootstrap Column class
17915 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17916 * @cfg {Boolean} carousel true to make the group behave like a carousel
17917 * @cfg {Boolean} bullets show bullets for the panels
17918 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17919 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17920 * @cfg {Boolean} showarrow (true|false) show arrow default true
17923 * Create a new TabGroup
17924 * @param {Object} config The config object
17927 Roo.bootstrap.TabGroup = function(config){
17928 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17930 this.navId = Roo.id();
17933 Roo.bootstrap.TabGroup.register(this);
17937 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17940 transition : false,
17945 slideOnTouch : false,
17948 getAutoCreate : function()
17950 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17952 cfg.cls += ' tab-content';
17954 if (this.carousel) {
17955 cfg.cls += ' carousel slide';
17958 cls : 'carousel-inner',
17962 if(this.bullets && !Roo.isTouch){
17965 cls : 'carousel-bullets',
17969 if(this.bullets_cls){
17970 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17977 cfg.cn[0].cn.push(bullets);
17980 if(this.showarrow){
17981 cfg.cn[0].cn.push({
17983 class : 'carousel-arrow',
17987 class : 'carousel-prev',
17991 class : 'fa fa-chevron-left'
17997 class : 'carousel-next',
18001 class : 'fa fa-chevron-right'
18014 initEvents: function()
18016 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18017 // this.el.on("touchstart", this.onTouchStart, this);
18020 if(this.autoslide){
18023 this.slideFn = window.setInterval(function() {
18024 _this.showPanelNext();
18028 if(this.showarrow){
18029 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18030 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18036 // onTouchStart : function(e, el, o)
18038 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18042 // this.showPanelNext();
18046 getChildContainer : function()
18048 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18052 * register a Navigation item
18053 * @param {Roo.bootstrap.NavItem} the navitem to add
18055 register : function(item)
18057 this.tabs.push( item);
18058 item.navId = this.navId; // not really needed..
18063 getActivePanel : function()
18066 Roo.each(this.tabs, function(t) {
18076 getPanelByName : function(n)
18079 Roo.each(this.tabs, function(t) {
18080 if (t.tabId == n) {
18088 indexOfPanel : function(p)
18091 Roo.each(this.tabs, function(t,i) {
18092 if (t.tabId == p.tabId) {
18101 * show a specific panel
18102 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18103 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18105 showPanel : function (pan)
18107 if(this.transition || typeof(pan) == 'undefined'){
18108 Roo.log("waiting for the transitionend");
18112 if (typeof(pan) == 'number') {
18113 pan = this.tabs[pan];
18116 if (typeof(pan) == 'string') {
18117 pan = this.getPanelByName(pan);
18120 var cur = this.getActivePanel();
18123 Roo.log('pan or acitve pan is undefined');
18127 if (pan.tabId == this.getActivePanel().tabId) {
18131 if (false === cur.fireEvent('beforedeactivate')) {
18135 if(this.bullets > 0 && !Roo.isTouch){
18136 this.setActiveBullet(this.indexOfPanel(pan));
18139 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18141 this.transition = true;
18142 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18143 var lr = dir == 'next' ? 'left' : 'right';
18144 pan.el.addClass(dir); // or prev
18145 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18146 cur.el.addClass(lr); // or right
18147 pan.el.addClass(lr);
18150 cur.el.on('transitionend', function() {
18151 Roo.log("trans end?");
18153 pan.el.removeClass([lr,dir]);
18154 pan.setActive(true);
18156 cur.el.removeClass([lr]);
18157 cur.setActive(false);
18159 _this.transition = false;
18161 }, this, { single: true } );
18166 cur.setActive(false);
18167 pan.setActive(true);
18172 showPanelNext : function()
18174 var i = this.indexOfPanel(this.getActivePanel());
18176 if (i >= this.tabs.length - 1 && !this.autoslide) {
18180 if (i >= this.tabs.length - 1 && this.autoslide) {
18184 this.showPanel(this.tabs[i+1]);
18187 showPanelPrev : function()
18189 var i = this.indexOfPanel(this.getActivePanel());
18191 if (i < 1 && !this.autoslide) {
18195 if (i < 1 && this.autoslide) {
18196 i = this.tabs.length;
18199 this.showPanel(this.tabs[i-1]);
18203 addBullet: function()
18205 if(!this.bullets || Roo.isTouch){
18208 var ctr = this.el.select('.carousel-bullets',true).first();
18209 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18210 var bullet = ctr.createChild({
18211 cls : 'bullet bullet-' + i
18212 },ctr.dom.lastChild);
18217 bullet.on('click', (function(e, el, o, ii, t){
18219 e.preventDefault();
18221 this.showPanel(ii);
18223 if(this.autoslide && this.slideFn){
18224 clearInterval(this.slideFn);
18225 this.slideFn = window.setInterval(function() {
18226 _this.showPanelNext();
18230 }).createDelegate(this, [i, bullet], true));
18235 setActiveBullet : function(i)
18241 Roo.each(this.el.select('.bullet', true).elements, function(el){
18242 el.removeClass('selected');
18245 var bullet = this.el.select('.bullet-' + i, true).first();
18251 bullet.addClass('selected');
18262 Roo.apply(Roo.bootstrap.TabGroup, {
18266 * register a Navigation Group
18267 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18269 register : function(navgrp)
18271 this.groups[navgrp.navId] = navgrp;
18275 * fetch a Navigation Group based on the navigation ID
18276 * if one does not exist , it will get created.
18277 * @param {string} the navgroup to add
18278 * @returns {Roo.bootstrap.NavGroup} the navgroup
18280 get: function(navId) {
18281 if (typeof(this.groups[navId]) == 'undefined') {
18282 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18284 return this.groups[navId] ;
18299 * @class Roo.bootstrap.TabPanel
18300 * @extends Roo.bootstrap.Component
18301 * Bootstrap TabPanel class
18302 * @cfg {Boolean} active panel active
18303 * @cfg {String} html panel content
18304 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18305 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18306 * @cfg {String} href click to link..
18310 * Create a new TabPanel
18311 * @param {Object} config The config object
18314 Roo.bootstrap.TabPanel = function(config){
18315 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18319 * Fires when the active status changes
18320 * @param {Roo.bootstrap.TabPanel} this
18321 * @param {Boolean} state the new state
18326 * @event beforedeactivate
18327 * Fires before a tab is de-activated - can be used to do validation on a form.
18328 * @param {Roo.bootstrap.TabPanel} this
18329 * @return {Boolean} false if there is an error
18332 'beforedeactivate': true
18335 this.tabId = this.tabId || Roo.id();
18339 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18347 getAutoCreate : function(){
18350 // item is needed for carousel - not sure if it has any effect otherwise
18351 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18352 html: this.html || ''
18356 cfg.cls += ' active';
18360 cfg.tabId = this.tabId;
18367 initEvents: function()
18369 var p = this.parent();
18371 this.navId = this.navId || p.navId;
18373 if (typeof(this.navId) != 'undefined') {
18374 // not really needed.. but just in case.. parent should be a NavGroup.
18375 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18379 var i = tg.tabs.length - 1;
18381 if(this.active && tg.bullets > 0 && i < tg.bullets){
18382 tg.setActiveBullet(i);
18386 this.el.on('click', this.onClick, this);
18389 this.el.on("touchstart", this.onTouchStart, this);
18390 this.el.on("touchmove", this.onTouchMove, this);
18391 this.el.on("touchend", this.onTouchEnd, this);
18396 onRender : function(ct, position)
18398 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18401 setActive : function(state)
18403 Roo.log("panel - set active " + this.tabId + "=" + state);
18405 this.active = state;
18407 this.el.removeClass('active');
18409 } else if (!this.el.hasClass('active')) {
18410 this.el.addClass('active');
18413 this.fireEvent('changed', this, state);
18416 onClick : function(e)
18418 e.preventDefault();
18420 if(!this.href.length){
18424 window.location.href = this.href;
18433 onTouchStart : function(e)
18435 this.swiping = false;
18437 this.startX = e.browserEvent.touches[0].clientX;
18438 this.startY = e.browserEvent.touches[0].clientY;
18441 onTouchMove : function(e)
18443 this.swiping = true;
18445 this.endX = e.browserEvent.touches[0].clientX;
18446 this.endY = e.browserEvent.touches[0].clientY;
18449 onTouchEnd : function(e)
18456 var tabGroup = this.parent();
18458 if(this.endX > this.startX){ // swiping right
18459 tabGroup.showPanelPrev();
18463 if(this.startX > this.endX){ // swiping left
18464 tabGroup.showPanelNext();
18483 * @class Roo.bootstrap.DateField
18484 * @extends Roo.bootstrap.Input
18485 * Bootstrap DateField class
18486 * @cfg {Number} weekStart default 0
18487 * @cfg {String} viewMode default empty, (months|years)
18488 * @cfg {String} minViewMode default empty, (months|years)
18489 * @cfg {Number} startDate default -Infinity
18490 * @cfg {Number} endDate default Infinity
18491 * @cfg {Boolean} todayHighlight default false
18492 * @cfg {Boolean} todayBtn default false
18493 * @cfg {Boolean} calendarWeeks default false
18494 * @cfg {Object} daysOfWeekDisabled default empty
18495 * @cfg {Boolean} singleMode default false (true | false)
18497 * @cfg {Boolean} keyboardNavigation default true
18498 * @cfg {String} language default en
18501 * Create a new DateField
18502 * @param {Object} config The config object
18505 Roo.bootstrap.DateField = function(config){
18506 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18510 * Fires when this field show.
18511 * @param {Roo.bootstrap.DateField} this
18512 * @param {Mixed} date The date value
18517 * Fires when this field hide.
18518 * @param {Roo.bootstrap.DateField} this
18519 * @param {Mixed} date The date value
18524 * Fires when select a date.
18525 * @param {Roo.bootstrap.DateField} this
18526 * @param {Mixed} date The date value
18530 * @event beforeselect
18531 * Fires when before select a date.
18532 * @param {Roo.bootstrap.DateField} this
18533 * @param {Mixed} date The date value
18535 beforeselect : true
18539 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18542 * @cfg {String} format
18543 * The default date format string which can be overriden for localization support. The format must be
18544 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18548 * @cfg {String} altFormats
18549 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18550 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18552 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18560 todayHighlight : false,
18566 keyboardNavigation: true,
18568 calendarWeeks: false,
18570 startDate: -Infinity,
18574 daysOfWeekDisabled: [],
18578 singleMode : false,
18580 UTCDate: function()
18582 return new Date(Date.UTC.apply(Date, arguments));
18585 UTCToday: function()
18587 var today = new Date();
18588 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18591 getDate: function() {
18592 var d = this.getUTCDate();
18593 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18596 getUTCDate: function() {
18600 setDate: function(d) {
18601 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18604 setUTCDate: function(d) {
18606 this.setValue(this.formatDate(this.date));
18609 onRender: function(ct, position)
18612 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18614 this.language = this.language || 'en';
18615 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18616 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18618 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18619 this.format = this.format || 'm/d/y';
18620 this.isInline = false;
18621 this.isInput = true;
18622 this.component = this.el.select('.add-on', true).first() || false;
18623 this.component = (this.component && this.component.length === 0) ? false : this.component;
18624 this.hasInput = this.component && this.inputEl().length;
18626 if (typeof(this.minViewMode === 'string')) {
18627 switch (this.minViewMode) {
18629 this.minViewMode = 1;
18632 this.minViewMode = 2;
18635 this.minViewMode = 0;
18640 if (typeof(this.viewMode === 'string')) {
18641 switch (this.viewMode) {
18654 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18656 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18658 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18660 this.picker().on('mousedown', this.onMousedown, this);
18661 this.picker().on('click', this.onClick, this);
18663 this.picker().addClass('datepicker-dropdown');
18665 this.startViewMode = this.viewMode;
18667 if(this.singleMode){
18668 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18669 v.setVisibilityMode(Roo.Element.DISPLAY);
18673 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18674 v.setStyle('width', '189px');
18678 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18679 if(!this.calendarWeeks){
18684 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18685 v.attr('colspan', function(i, val){
18686 return parseInt(val) + 1;
18691 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18693 this.setStartDate(this.startDate);
18694 this.setEndDate(this.endDate);
18696 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18703 if(this.isInline) {
18708 picker : function()
18710 return this.pickerEl;
18711 // return this.el.select('.datepicker', true).first();
18714 fillDow: function()
18716 var dowCnt = this.weekStart;
18725 if(this.calendarWeeks){
18733 while (dowCnt < this.weekStart + 7) {
18737 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18741 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18744 fillMonths: function()
18747 var months = this.picker().select('>.datepicker-months td', true).first();
18749 months.dom.innerHTML = '';
18755 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18758 months.createChild(month);
18765 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;
18767 if (this.date < this.startDate) {
18768 this.viewDate = new Date(this.startDate);
18769 } else if (this.date > this.endDate) {
18770 this.viewDate = new Date(this.endDate);
18772 this.viewDate = new Date(this.date);
18780 var d = new Date(this.viewDate),
18781 year = d.getUTCFullYear(),
18782 month = d.getUTCMonth(),
18783 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18784 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18785 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18786 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18787 currentDate = this.date && this.date.valueOf(),
18788 today = this.UTCToday();
18790 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18792 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18794 // this.picker.select('>tfoot th.today').
18795 // .text(dates[this.language].today)
18796 // .toggle(this.todayBtn !== false);
18798 this.updateNavArrows();
18801 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18803 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18805 prevMonth.setUTCDate(day);
18807 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18809 var nextMonth = new Date(prevMonth);
18811 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18813 nextMonth = nextMonth.valueOf();
18815 var fillMonths = false;
18817 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18819 while(prevMonth.valueOf() <= nextMonth) {
18822 if (prevMonth.getUTCDay() === this.weekStart) {
18824 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18832 if(this.calendarWeeks){
18833 // ISO 8601: First week contains first thursday.
18834 // ISO also states week starts on Monday, but we can be more abstract here.
18836 // Start of current week: based on weekstart/current date
18837 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18838 // Thursday of this week
18839 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18840 // First Thursday of year, year from thursday
18841 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18842 // Calendar week: ms between thursdays, div ms per day, div 7 days
18843 calWeek = (th - yth) / 864e5 / 7 + 1;
18845 fillMonths.cn.push({
18853 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18855 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18858 if (this.todayHighlight &&
18859 prevMonth.getUTCFullYear() == today.getFullYear() &&
18860 prevMonth.getUTCMonth() == today.getMonth() &&
18861 prevMonth.getUTCDate() == today.getDate()) {
18862 clsName += ' today';
18865 if (currentDate && prevMonth.valueOf() === currentDate) {
18866 clsName += ' active';
18869 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18870 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18871 clsName += ' disabled';
18874 fillMonths.cn.push({
18876 cls: 'day ' + clsName,
18877 html: prevMonth.getDate()
18880 prevMonth.setDate(prevMonth.getDate()+1);
18883 var currentYear = this.date && this.date.getUTCFullYear();
18884 var currentMonth = this.date && this.date.getUTCMonth();
18886 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18888 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18889 v.removeClass('active');
18891 if(currentYear === year && k === currentMonth){
18892 v.addClass('active');
18895 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18896 v.addClass('disabled');
18902 year = parseInt(year/10, 10) * 10;
18904 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18906 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18909 for (var i = -1; i < 11; i++) {
18910 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18912 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18920 showMode: function(dir)
18923 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18926 Roo.each(this.picker().select('>div',true).elements, function(v){
18927 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18930 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18935 if(this.isInline) {
18939 this.picker().removeClass(['bottom', 'top']);
18941 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18943 * place to the top of element!
18947 this.picker().addClass('top');
18948 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18953 this.picker().addClass('bottom');
18955 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18958 parseDate : function(value)
18960 if(!value || value instanceof Date){
18963 var v = Date.parseDate(value, this.format);
18964 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18965 v = Date.parseDate(value, 'Y-m-d');
18967 if(!v && this.altFormats){
18968 if(!this.altFormatsArray){
18969 this.altFormatsArray = this.altFormats.split("|");
18971 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18972 v = Date.parseDate(value, this.altFormatsArray[i]);
18978 formatDate : function(date, fmt)
18980 return (!date || !(date instanceof Date)) ?
18981 date : date.dateFormat(fmt || this.format);
18984 onFocus : function()
18986 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18990 onBlur : function()
18992 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18994 var d = this.inputEl().getValue();
19001 showPopup : function()
19003 this.picker().show();
19007 this.fireEvent('showpopup', this, this.date);
19010 hidePopup : function()
19012 if(this.isInline) {
19015 this.picker().hide();
19016 this.viewMode = this.startViewMode;
19019 this.fireEvent('hidepopup', this, this.date);
19023 onMousedown: function(e)
19025 e.stopPropagation();
19026 e.preventDefault();
19031 Roo.bootstrap.DateField.superclass.keyup.call(this);
19035 setValue: function(v)
19037 if(this.fireEvent('beforeselect', this, v) !== false){
19038 var d = new Date(this.parseDate(v) ).clearTime();
19040 if(isNaN(d.getTime())){
19041 this.date = this.viewDate = '';
19042 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19046 v = this.formatDate(d);
19048 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19050 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19054 this.fireEvent('select', this, this.date);
19058 getValue: function()
19060 return this.formatDate(this.date);
19063 fireKey: function(e)
19065 if (!this.picker().isVisible()){
19066 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19072 var dateChanged = false,
19074 newDate, newViewDate;
19079 e.preventDefault();
19083 if (!this.keyboardNavigation) {
19086 dir = e.keyCode == 37 ? -1 : 1;
19089 newDate = this.moveYear(this.date, dir);
19090 newViewDate = this.moveYear(this.viewDate, dir);
19091 } else if (e.shiftKey){
19092 newDate = this.moveMonth(this.date, dir);
19093 newViewDate = this.moveMonth(this.viewDate, dir);
19095 newDate = new Date(this.date);
19096 newDate.setUTCDate(this.date.getUTCDate() + dir);
19097 newViewDate = new Date(this.viewDate);
19098 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19100 if (this.dateWithinRange(newDate)){
19101 this.date = newDate;
19102 this.viewDate = newViewDate;
19103 this.setValue(this.formatDate(this.date));
19105 e.preventDefault();
19106 dateChanged = true;
19111 if (!this.keyboardNavigation) {
19114 dir = e.keyCode == 38 ? -1 : 1;
19116 newDate = this.moveYear(this.date, dir);
19117 newViewDate = this.moveYear(this.viewDate, dir);
19118 } else if (e.shiftKey){
19119 newDate = this.moveMonth(this.date, dir);
19120 newViewDate = this.moveMonth(this.viewDate, dir);
19122 newDate = new Date(this.date);
19123 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19124 newViewDate = new Date(this.viewDate);
19125 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19127 if (this.dateWithinRange(newDate)){
19128 this.date = newDate;
19129 this.viewDate = newViewDate;
19130 this.setValue(this.formatDate(this.date));
19132 e.preventDefault();
19133 dateChanged = true;
19137 this.setValue(this.formatDate(this.date));
19139 e.preventDefault();
19142 this.setValue(this.formatDate(this.date));
19156 onClick: function(e)
19158 e.stopPropagation();
19159 e.preventDefault();
19161 var target = e.getTarget();
19163 if(target.nodeName.toLowerCase() === 'i'){
19164 target = Roo.get(target).dom.parentNode;
19167 var nodeName = target.nodeName;
19168 var className = target.className;
19169 var html = target.innerHTML;
19170 //Roo.log(nodeName);
19172 switch(nodeName.toLowerCase()) {
19174 switch(className) {
19180 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19181 switch(this.viewMode){
19183 this.viewDate = this.moveMonth(this.viewDate, dir);
19187 this.viewDate = this.moveYear(this.viewDate, dir);
19193 var date = new Date();
19194 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19196 this.setValue(this.formatDate(this.date));
19203 if (className.indexOf('disabled') < 0) {
19204 this.viewDate.setUTCDate(1);
19205 if (className.indexOf('month') > -1) {
19206 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19208 var year = parseInt(html, 10) || 0;
19209 this.viewDate.setUTCFullYear(year);
19213 if(this.singleMode){
19214 this.setValue(this.formatDate(this.viewDate));
19225 //Roo.log(className);
19226 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19227 var day = parseInt(html, 10) || 1;
19228 var year = this.viewDate.getUTCFullYear(),
19229 month = this.viewDate.getUTCMonth();
19231 if (className.indexOf('old') > -1) {
19238 } else if (className.indexOf('new') > -1) {
19246 //Roo.log([year,month,day]);
19247 this.date = this.UTCDate(year, month, day,0,0,0,0);
19248 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19250 //Roo.log(this.formatDate(this.date));
19251 this.setValue(this.formatDate(this.date));
19258 setStartDate: function(startDate)
19260 this.startDate = startDate || -Infinity;
19261 if (this.startDate !== -Infinity) {
19262 this.startDate = this.parseDate(this.startDate);
19265 this.updateNavArrows();
19268 setEndDate: function(endDate)
19270 this.endDate = endDate || Infinity;
19271 if (this.endDate !== Infinity) {
19272 this.endDate = this.parseDate(this.endDate);
19275 this.updateNavArrows();
19278 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19280 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19281 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19282 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19284 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19285 return parseInt(d, 10);
19288 this.updateNavArrows();
19291 updateNavArrows: function()
19293 if(this.singleMode){
19297 var d = new Date(this.viewDate),
19298 year = d.getUTCFullYear(),
19299 month = d.getUTCMonth();
19301 Roo.each(this.picker().select('.prev', true).elements, function(v){
19303 switch (this.viewMode) {
19306 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19312 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19319 Roo.each(this.picker().select('.next', true).elements, function(v){
19321 switch (this.viewMode) {
19324 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19330 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19338 moveMonth: function(date, dir)
19343 var new_date = new Date(date.valueOf()),
19344 day = new_date.getUTCDate(),
19345 month = new_date.getUTCMonth(),
19346 mag = Math.abs(dir),
19348 dir = dir > 0 ? 1 : -1;
19351 // If going back one month, make sure month is not current month
19352 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19354 return new_date.getUTCMonth() == month;
19356 // If going forward one month, make sure month is as expected
19357 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19359 return new_date.getUTCMonth() != new_month;
19361 new_month = month + dir;
19362 new_date.setUTCMonth(new_month);
19363 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19364 if (new_month < 0 || new_month > 11) {
19365 new_month = (new_month + 12) % 12;
19368 // For magnitudes >1, move one month at a time...
19369 for (var i=0; i<mag; i++) {
19370 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19371 new_date = this.moveMonth(new_date, dir);
19373 // ...then reset the day, keeping it in the new month
19374 new_month = new_date.getUTCMonth();
19375 new_date.setUTCDate(day);
19377 return new_month != new_date.getUTCMonth();
19380 // Common date-resetting loop -- if date is beyond end of month, make it
19383 new_date.setUTCDate(--day);
19384 new_date.setUTCMonth(new_month);
19389 moveYear: function(date, dir)
19391 return this.moveMonth(date, dir*12);
19394 dateWithinRange: function(date)
19396 return date >= this.startDate && date <= this.endDate;
19402 this.picker().remove();
19405 validateValue : function(value)
19407 if(this.getVisibilityEl().hasClass('hidden')){
19411 if(value.length < 1) {
19412 if(this.allowBlank){
19418 if(value.length < this.minLength){
19421 if(value.length > this.maxLength){
19425 var vt = Roo.form.VTypes;
19426 if(!vt[this.vtype](value, this)){
19430 if(typeof this.validator == "function"){
19431 var msg = this.validator(value);
19437 if(this.regex && !this.regex.test(value)){
19441 if(typeof(this.parseDate(value)) == 'undefined'){
19445 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19449 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19459 this.date = this.viewDate = '';
19461 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19466 Roo.apply(Roo.bootstrap.DateField, {
19477 html: '<i class="fa fa-arrow-left"/>'
19487 html: '<i class="fa fa-arrow-right"/>'
19529 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19530 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19531 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19532 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19533 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19546 navFnc: 'FullYear',
19551 navFnc: 'FullYear',
19556 Roo.apply(Roo.bootstrap.DateField, {
19560 cls: 'datepicker dropdown-menu roo-dynamic',
19564 cls: 'datepicker-days',
19568 cls: 'table-condensed',
19570 Roo.bootstrap.DateField.head,
19574 Roo.bootstrap.DateField.footer
19581 cls: 'datepicker-months',
19585 cls: 'table-condensed',
19587 Roo.bootstrap.DateField.head,
19588 Roo.bootstrap.DateField.content,
19589 Roo.bootstrap.DateField.footer
19596 cls: 'datepicker-years',
19600 cls: 'table-condensed',
19602 Roo.bootstrap.DateField.head,
19603 Roo.bootstrap.DateField.content,
19604 Roo.bootstrap.DateField.footer
19623 * @class Roo.bootstrap.TimeField
19624 * @extends Roo.bootstrap.Input
19625 * Bootstrap DateField class
19629 * Create a new TimeField
19630 * @param {Object} config The config object
19633 Roo.bootstrap.TimeField = function(config){
19634 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19638 * Fires when this field show.
19639 * @param {Roo.bootstrap.DateField} thisthis
19640 * @param {Mixed} date The date value
19645 * Fires when this field hide.
19646 * @param {Roo.bootstrap.DateField} this
19647 * @param {Mixed} date The date value
19652 * Fires when select a date.
19653 * @param {Roo.bootstrap.DateField} this
19654 * @param {Mixed} date The date value
19660 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19663 * @cfg {String} format
19664 * The default time format string which can be overriden for localization support. The format must be
19665 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19669 onRender: function(ct, position)
19672 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19674 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19676 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19678 this.pop = this.picker().select('>.datepicker-time',true).first();
19679 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19681 this.picker().on('mousedown', this.onMousedown, this);
19682 this.picker().on('click', this.onClick, this);
19684 this.picker().addClass('datepicker-dropdown');
19689 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19690 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19691 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19692 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19693 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19694 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19698 fireKey: function(e){
19699 if (!this.picker().isVisible()){
19700 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19706 e.preventDefault();
19714 this.onTogglePeriod();
19717 this.onIncrementMinutes();
19720 this.onDecrementMinutes();
19729 onClick: function(e) {
19730 e.stopPropagation();
19731 e.preventDefault();
19734 picker : function()
19736 return this.el.select('.datepicker', true).first();
19739 fillTime: function()
19741 var time = this.pop.select('tbody', true).first();
19743 time.dom.innerHTML = '';
19758 cls: 'hours-up glyphicon glyphicon-chevron-up'
19778 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19799 cls: 'timepicker-hour',
19814 cls: 'timepicker-minute',
19829 cls: 'btn btn-primary period',
19851 cls: 'hours-down glyphicon glyphicon-chevron-down'
19871 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19889 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19896 var hours = this.time.getHours();
19897 var minutes = this.time.getMinutes();
19910 hours = hours - 12;
19914 hours = '0' + hours;
19918 minutes = '0' + minutes;
19921 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19922 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19923 this.pop.select('button', true).first().dom.innerHTML = period;
19929 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19931 var cls = ['bottom'];
19933 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19940 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19945 this.picker().addClass(cls.join('-'));
19949 Roo.each(cls, function(c){
19951 _this.picker().setTop(_this.inputEl().getHeight());
19955 _this.picker().setTop(0 - _this.picker().getHeight());
19960 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19964 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19971 onFocus : function()
19973 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19977 onBlur : function()
19979 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19985 this.picker().show();
19990 this.fireEvent('show', this, this.date);
19995 this.picker().hide();
19998 this.fireEvent('hide', this, this.date);
20001 setTime : function()
20004 this.setValue(this.time.format(this.format));
20006 this.fireEvent('select', this, this.date);
20011 onMousedown: function(e){
20012 e.stopPropagation();
20013 e.preventDefault();
20016 onIncrementHours: function()
20018 Roo.log('onIncrementHours');
20019 this.time = this.time.add(Date.HOUR, 1);
20024 onDecrementHours: function()
20026 Roo.log('onDecrementHours');
20027 this.time = this.time.add(Date.HOUR, -1);
20031 onIncrementMinutes: function()
20033 Roo.log('onIncrementMinutes');
20034 this.time = this.time.add(Date.MINUTE, 1);
20038 onDecrementMinutes: function()
20040 Roo.log('onDecrementMinutes');
20041 this.time = this.time.add(Date.MINUTE, -1);
20045 onTogglePeriod: function()
20047 Roo.log('onTogglePeriod');
20048 this.time = this.time.add(Date.HOUR, 12);
20055 Roo.apply(Roo.bootstrap.TimeField, {
20085 cls: 'btn btn-info ok',
20097 Roo.apply(Roo.bootstrap.TimeField, {
20101 cls: 'datepicker dropdown-menu',
20105 cls: 'datepicker-time',
20109 cls: 'table-condensed',
20111 Roo.bootstrap.TimeField.content,
20112 Roo.bootstrap.TimeField.footer
20131 * @class Roo.bootstrap.MonthField
20132 * @extends Roo.bootstrap.Input
20133 * Bootstrap MonthField class
20135 * @cfg {String} language default en
20138 * Create a new MonthField
20139 * @param {Object} config The config object
20142 Roo.bootstrap.MonthField = function(config){
20143 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20148 * Fires when this field show.
20149 * @param {Roo.bootstrap.MonthField} this
20150 * @param {Mixed} date The date value
20155 * Fires when this field hide.
20156 * @param {Roo.bootstrap.MonthField} this
20157 * @param {Mixed} date The date value
20162 * Fires when select a date.
20163 * @param {Roo.bootstrap.MonthField} this
20164 * @param {String} oldvalue The old value
20165 * @param {String} newvalue The new value
20171 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20173 onRender: function(ct, position)
20176 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20178 this.language = this.language || 'en';
20179 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20180 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20182 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20183 this.isInline = false;
20184 this.isInput = true;
20185 this.component = this.el.select('.add-on', true).first() || false;
20186 this.component = (this.component && this.component.length === 0) ? false : this.component;
20187 this.hasInput = this.component && this.inputEL().length;
20189 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20191 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20193 this.picker().on('mousedown', this.onMousedown, this);
20194 this.picker().on('click', this.onClick, this);
20196 this.picker().addClass('datepicker-dropdown');
20198 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20199 v.setStyle('width', '189px');
20206 if(this.isInline) {
20212 setValue: function(v, suppressEvent)
20214 var o = this.getValue();
20216 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20220 if(suppressEvent !== true){
20221 this.fireEvent('select', this, o, v);
20226 getValue: function()
20231 onClick: function(e)
20233 e.stopPropagation();
20234 e.preventDefault();
20236 var target = e.getTarget();
20238 if(target.nodeName.toLowerCase() === 'i'){
20239 target = Roo.get(target).dom.parentNode;
20242 var nodeName = target.nodeName;
20243 var className = target.className;
20244 var html = target.innerHTML;
20246 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20250 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20252 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20258 picker : function()
20260 return this.pickerEl;
20263 fillMonths: function()
20266 var months = this.picker().select('>.datepicker-months td', true).first();
20268 months.dom.innerHTML = '';
20274 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20277 months.createChild(month);
20286 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20287 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20290 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20291 e.removeClass('active');
20293 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20294 e.addClass('active');
20301 if(this.isInline) {
20305 this.picker().removeClass(['bottom', 'top']);
20307 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20309 * place to the top of element!
20313 this.picker().addClass('top');
20314 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20319 this.picker().addClass('bottom');
20321 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20324 onFocus : function()
20326 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20330 onBlur : function()
20332 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20334 var d = this.inputEl().getValue();
20343 this.picker().show();
20344 this.picker().select('>.datepicker-months', true).first().show();
20348 this.fireEvent('show', this, this.date);
20353 if(this.isInline) {
20356 this.picker().hide();
20357 this.fireEvent('hide', this, this.date);
20361 onMousedown: function(e)
20363 e.stopPropagation();
20364 e.preventDefault();
20369 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20373 fireKey: function(e)
20375 if (!this.picker().isVisible()){
20376 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20387 e.preventDefault();
20391 dir = e.keyCode == 37 ? -1 : 1;
20393 this.vIndex = this.vIndex + dir;
20395 if(this.vIndex < 0){
20399 if(this.vIndex > 11){
20403 if(isNaN(this.vIndex)){
20407 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20413 dir = e.keyCode == 38 ? -1 : 1;
20415 this.vIndex = this.vIndex + dir * 4;
20417 if(this.vIndex < 0){
20421 if(this.vIndex > 11){
20425 if(isNaN(this.vIndex)){
20429 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20434 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20435 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20439 e.preventDefault();
20442 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20443 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20459 this.picker().remove();
20464 Roo.apply(Roo.bootstrap.MonthField, {
20483 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20484 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20489 Roo.apply(Roo.bootstrap.MonthField, {
20493 cls: 'datepicker dropdown-menu roo-dynamic',
20497 cls: 'datepicker-months',
20501 cls: 'table-condensed',
20503 Roo.bootstrap.DateField.content
20523 * @class Roo.bootstrap.CheckBox
20524 * @extends Roo.bootstrap.Input
20525 * Bootstrap CheckBox class
20527 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20528 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20529 * @cfg {String} boxLabel The text that appears beside the checkbox
20530 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20531 * @cfg {Boolean} checked initnal the element
20532 * @cfg {Boolean} inline inline the element (default false)
20533 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20534 * @cfg {String} tooltip label tooltip
20537 * Create a new CheckBox
20538 * @param {Object} config The config object
20541 Roo.bootstrap.CheckBox = function(config){
20542 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20547 * Fires when the element is checked or unchecked.
20548 * @param {Roo.bootstrap.CheckBox} this This input
20549 * @param {Boolean} checked The new checked value
20554 * Fires when the element is click.
20555 * @param {Roo.bootstrap.CheckBox} this This input
20562 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20564 inputType: 'checkbox',
20573 getAutoCreate : function()
20575 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20581 cfg.cls = 'form-group ' + this.inputType; //input-group
20584 cfg.cls += ' ' + this.inputType + '-inline';
20590 type : this.inputType,
20591 value : this.inputValue,
20592 cls : 'roo-' + this.inputType, //'form-box',
20593 placeholder : this.placeholder || ''
20597 if(this.inputType != 'radio'){
20601 cls : 'roo-hidden-value',
20602 value : this.checked ? this.inputValue : this.valueOff
20607 if (this.weight) { // Validity check?
20608 cfg.cls += " " + this.inputType + "-" + this.weight;
20611 if (this.disabled) {
20612 input.disabled=true;
20616 input.checked = this.checked;
20621 input.name = this.name;
20623 if(this.inputType != 'radio'){
20624 hidden.name = this.name;
20625 input.name = '_hidden_' + this.name;
20630 input.cls += ' input-' + this.size;
20635 ['xs','sm','md','lg'].map(function(size){
20636 if (settings[size]) {
20637 cfg.cls += ' col-' + size + '-' + settings[size];
20641 var inputblock = input;
20643 if (this.before || this.after) {
20646 cls : 'input-group',
20651 inputblock.cn.push({
20653 cls : 'input-group-addon',
20658 inputblock.cn.push(input);
20660 if(this.inputType != 'radio'){
20661 inputblock.cn.push(hidden);
20665 inputblock.cn.push({
20667 cls : 'input-group-addon',
20674 if (align ==='left' && this.fieldLabel.length) {
20675 // Roo.log("left and has label");
20680 cls : 'control-label',
20681 html : this.fieldLabel
20691 if(this.labelWidth > 12){
20692 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20695 if(this.labelWidth < 13 && this.labelmd == 0){
20696 this.labelmd = this.labelWidth;
20699 if(this.labellg > 0){
20700 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20701 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20704 if(this.labelmd > 0){
20705 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20706 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20709 if(this.labelsm > 0){
20710 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20711 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20714 if(this.labelxs > 0){
20715 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20716 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20719 } else if ( this.fieldLabel.length) {
20720 // Roo.log(" label");
20724 tag: this.boxLabel ? 'span' : 'label',
20726 cls: 'control-label box-input-label',
20727 //cls : 'input-group-addon',
20728 html : this.fieldLabel
20737 // Roo.log(" no label && no align");
20738 cfg.cn = [ inputblock ] ;
20744 var boxLabelCfg = {
20746 //'for': id, // box label is handled by onclick - so no for...
20748 html: this.boxLabel
20752 boxLabelCfg.tooltip = this.tooltip;
20755 cfg.cn.push(boxLabelCfg);
20758 if(this.inputType != 'radio'){
20759 cfg.cn.push(hidden);
20767 * return the real input element.
20769 inputEl: function ()
20771 return this.el.select('input.roo-' + this.inputType,true).first();
20773 hiddenEl: function ()
20775 return this.el.select('input.roo-hidden-value',true).first();
20778 labelEl: function()
20780 return this.el.select('label.control-label',true).first();
20782 /* depricated... */
20786 return this.labelEl();
20789 boxLabelEl: function()
20791 return this.el.select('label.box-label',true).first();
20794 initEvents : function()
20796 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20798 this.inputEl().on('click', this.onClick, this);
20800 if (this.boxLabel) {
20801 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20804 this.startValue = this.getValue();
20807 Roo.bootstrap.CheckBox.register(this);
20811 onClick : function(e)
20813 if(this.fireEvent('click', this, e) !== false){
20814 this.setChecked(!this.checked);
20819 setChecked : function(state,suppressEvent)
20821 this.startValue = this.getValue();
20823 if(this.inputType == 'radio'){
20825 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20826 e.dom.checked = false;
20829 this.inputEl().dom.checked = true;
20831 this.inputEl().dom.value = this.inputValue;
20833 if(suppressEvent !== true){
20834 this.fireEvent('check', this, true);
20842 this.checked = state;
20844 this.inputEl().dom.checked = state;
20847 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20849 if(suppressEvent !== true){
20850 this.fireEvent('check', this, state);
20856 getValue : function()
20858 if(this.inputType == 'radio'){
20859 return this.getGroupValue();
20862 return this.hiddenEl().dom.value;
20866 getGroupValue : function()
20868 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20872 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20875 setValue : function(v,suppressEvent)
20877 if(this.inputType == 'radio'){
20878 this.setGroupValue(v, suppressEvent);
20882 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20887 setGroupValue : function(v, suppressEvent)
20889 this.startValue = this.getValue();
20891 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20892 e.dom.checked = false;
20894 if(e.dom.value == v){
20895 e.dom.checked = true;
20899 if(suppressEvent !== true){
20900 this.fireEvent('check', this, true);
20908 validate : function()
20910 if(this.getVisibilityEl().hasClass('hidden')){
20916 (this.inputType == 'radio' && this.validateRadio()) ||
20917 (this.inputType == 'checkbox' && this.validateCheckbox())
20923 this.markInvalid();
20927 validateRadio : function()
20929 if(this.getVisibilityEl().hasClass('hidden')){
20933 if(this.allowBlank){
20939 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20940 if(!e.dom.checked){
20952 validateCheckbox : function()
20955 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20956 //return (this.getValue() == this.inputValue) ? true : false;
20959 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20967 for(var i in group){
20968 if(group[i].el.isVisible(true)){
20976 for(var i in group){
20981 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20988 * Mark this field as valid
20990 markValid : function()
20994 this.fireEvent('valid', this);
20996 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20999 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21006 if(this.inputType == 'radio'){
21007 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21008 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21009 e.findParent('.form-group', false, true).addClass(_this.validClass);
21016 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21017 this.el.findParent('.form-group', false, true).addClass(this.validClass);
21021 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21027 for(var i in group){
21028 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21029 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21034 * Mark this field as invalid
21035 * @param {String} msg The validation message
21037 markInvalid : function(msg)
21039 if(this.allowBlank){
21045 this.fireEvent('invalid', this, msg);
21047 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21050 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21054 label.markInvalid();
21057 if(this.inputType == 'radio'){
21058 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21059 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21060 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21067 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21068 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21072 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21078 for(var i in group){
21079 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21080 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21085 clearInvalid : function()
21087 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21089 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21091 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21093 if (label && label.iconEl) {
21094 label.iconEl.removeClass(label.validClass);
21095 label.iconEl.removeClass(label.invalidClass);
21099 disable : function()
21101 if(this.inputType != 'radio'){
21102 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21109 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21110 _this.getActionEl().addClass(this.disabledClass);
21111 e.dom.disabled = true;
21115 this.disabled = true;
21116 this.fireEvent("disable", this);
21120 enable : function()
21122 if(this.inputType != 'radio'){
21123 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21130 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21131 _this.getActionEl().removeClass(this.disabledClass);
21132 e.dom.disabled = false;
21136 this.disabled = false;
21137 this.fireEvent("enable", this);
21141 setBoxLabel : function(v)
21146 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21152 Roo.apply(Roo.bootstrap.CheckBox, {
21157 * register a CheckBox Group
21158 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21160 register : function(checkbox)
21162 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21163 this.groups[checkbox.groupId] = {};
21166 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21170 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21174 * fetch a CheckBox Group based on the group ID
21175 * @param {string} the group ID
21176 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21178 get: function(groupId) {
21179 if (typeof(this.groups[groupId]) == 'undefined') {
21183 return this.groups[groupId] ;
21196 * @class Roo.bootstrap.Radio
21197 * @extends Roo.bootstrap.Component
21198 * Bootstrap Radio class
21199 * @cfg {String} boxLabel - the label associated
21200 * @cfg {String} value - the value of radio
21203 * Create a new Radio
21204 * @param {Object} config The config object
21206 Roo.bootstrap.Radio = function(config){
21207 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21211 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21217 getAutoCreate : function()
21221 cls : 'form-group radio',
21226 html : this.boxLabel
21234 initEvents : function()
21236 this.parent().register(this);
21238 this.el.on('click', this.onClick, this);
21242 onClick : function(e)
21244 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21245 this.setChecked(true);
21249 setChecked : function(state, suppressEvent)
21251 this.parent().setValue(this.value, suppressEvent);
21255 setBoxLabel : function(v)
21260 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21275 * @class Roo.bootstrap.SecurePass
21276 * @extends Roo.bootstrap.Input
21277 * Bootstrap SecurePass class
21281 * Create a new SecurePass
21282 * @param {Object} config The config object
21285 Roo.bootstrap.SecurePass = function (config) {
21286 // these go here, so the translation tool can replace them..
21288 PwdEmpty: "Please type a password, and then retype it to confirm.",
21289 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21290 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21291 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21292 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21293 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21294 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21295 TooWeak: "Your password is Too Weak."
21297 this.meterLabel = "Password strength:";
21298 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21299 this.meterClass = [
21300 "roo-password-meter-tooweak",
21301 "roo-password-meter-weak",
21302 "roo-password-meter-medium",
21303 "roo-password-meter-strong",
21304 "roo-password-meter-grey"
21309 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21312 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21314 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21316 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21317 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21318 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21319 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21320 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21321 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21322 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21332 * @cfg {String/Object} Label for the strength meter (defaults to
21333 * 'Password strength:')
21338 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21339 * ['Weak', 'Medium', 'Strong'])
21342 pwdStrengths: false,
21355 initEvents: function ()
21357 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21359 if (this.el.is('input[type=password]') && Roo.isSafari) {
21360 this.el.on('keydown', this.SafariOnKeyDown, this);
21363 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21366 onRender: function (ct, position)
21368 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21369 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21370 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21372 this.trigger.createChild({
21377 cls: 'roo-password-meter-grey col-xs-12',
21380 //width: this.meterWidth + 'px'
21384 cls: 'roo-password-meter-text'
21390 if (this.hideTrigger) {
21391 this.trigger.setDisplayed(false);
21393 this.setSize(this.width || '', this.height || '');
21396 onDestroy: function ()
21398 if (this.trigger) {
21399 this.trigger.removeAllListeners();
21400 this.trigger.remove();
21403 this.wrap.remove();
21405 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21408 checkStrength: function ()
21410 var pwd = this.inputEl().getValue();
21411 if (pwd == this._lastPwd) {
21416 if (this.ClientSideStrongPassword(pwd)) {
21418 } else if (this.ClientSideMediumPassword(pwd)) {
21420 } else if (this.ClientSideWeakPassword(pwd)) {
21426 Roo.log('strength1: ' + strength);
21428 //var pm = this.trigger.child('div/div/div').dom;
21429 var pm = this.trigger.child('div/div');
21430 pm.removeClass(this.meterClass);
21431 pm.addClass(this.meterClass[strength]);
21434 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21436 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21438 this._lastPwd = pwd;
21442 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21444 this._lastPwd = '';
21446 var pm = this.trigger.child('div/div');
21447 pm.removeClass(this.meterClass);
21448 pm.addClass('roo-password-meter-grey');
21451 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21454 this.inputEl().dom.type='password';
21457 validateValue: function (value)
21460 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21463 if (value.length == 0) {
21464 if (this.allowBlank) {
21465 this.clearInvalid();
21469 this.markInvalid(this.errors.PwdEmpty);
21470 this.errorMsg = this.errors.PwdEmpty;
21478 if ('[\x21-\x7e]*'.match(value)) {
21479 this.markInvalid(this.errors.PwdBadChar);
21480 this.errorMsg = this.errors.PwdBadChar;
21483 if (value.length < 6) {
21484 this.markInvalid(this.errors.PwdShort);
21485 this.errorMsg = this.errors.PwdShort;
21488 if (value.length > 16) {
21489 this.markInvalid(this.errors.PwdLong);
21490 this.errorMsg = this.errors.PwdLong;
21494 if (this.ClientSideStrongPassword(value)) {
21496 } else if (this.ClientSideMediumPassword(value)) {
21498 } else if (this.ClientSideWeakPassword(value)) {
21505 if (strength < 2) {
21506 //this.markInvalid(this.errors.TooWeak);
21507 this.errorMsg = this.errors.TooWeak;
21512 console.log('strength2: ' + strength);
21514 //var pm = this.trigger.child('div/div/div').dom;
21516 var pm = this.trigger.child('div/div');
21517 pm.removeClass(this.meterClass);
21518 pm.addClass(this.meterClass[strength]);
21520 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21522 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21524 this.errorMsg = '';
21528 CharacterSetChecks: function (type)
21531 this.fResult = false;
21534 isctype: function (character, type)
21537 case this.kCapitalLetter:
21538 if (character >= 'A' && character <= 'Z') {
21543 case this.kSmallLetter:
21544 if (character >= 'a' && character <= 'z') {
21550 if (character >= '0' && character <= '9') {
21555 case this.kPunctuation:
21556 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21567 IsLongEnough: function (pwd, size)
21569 return !(pwd == null || isNaN(size) || pwd.length < size);
21572 SpansEnoughCharacterSets: function (word, nb)
21574 if (!this.IsLongEnough(word, nb))
21579 var characterSetChecks = new Array(
21580 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21581 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21584 for (var index = 0; index < word.length; ++index) {
21585 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21586 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21587 characterSetChecks[nCharSet].fResult = true;
21594 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21595 if (characterSetChecks[nCharSet].fResult) {
21600 if (nCharSets < nb) {
21606 ClientSideStrongPassword: function (pwd)
21608 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21611 ClientSideMediumPassword: function (pwd)
21613 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21616 ClientSideWeakPassword: function (pwd)
21618 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21621 })//<script type="text/javascript">
21624 * Based Ext JS Library 1.1.1
21625 * Copyright(c) 2006-2007, Ext JS, LLC.
21631 * @class Roo.HtmlEditorCore
21632 * @extends Roo.Component
21633 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21635 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21638 Roo.HtmlEditorCore = function(config){
21641 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21646 * @event initialize
21647 * Fires when the editor is fully initialized (including the iframe)
21648 * @param {Roo.HtmlEditorCore} this
21653 * Fires when the editor is first receives the focus. Any insertion must wait
21654 * until after this event.
21655 * @param {Roo.HtmlEditorCore} this
21659 * @event beforesync
21660 * Fires before the textarea is updated with content from the editor iframe. Return false
21661 * to cancel the sync.
21662 * @param {Roo.HtmlEditorCore} this
21663 * @param {String} html
21667 * @event beforepush
21668 * Fires before the iframe editor is updated with content from the textarea. Return false
21669 * to cancel the push.
21670 * @param {Roo.HtmlEditorCore} this
21671 * @param {String} html
21676 * Fires when the textarea is updated with content from the editor iframe.
21677 * @param {Roo.HtmlEditorCore} this
21678 * @param {String} html
21683 * Fires when the iframe editor is updated with content from the textarea.
21684 * @param {Roo.HtmlEditorCore} this
21685 * @param {String} html
21690 * @event editorevent
21691 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21692 * @param {Roo.HtmlEditorCore} this
21698 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21700 // defaults : white / black...
21701 this.applyBlacklists();
21708 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21712 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21718 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21723 * @cfg {Number} height (in pixels)
21727 * @cfg {Number} width (in pixels)
21732 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21735 stylesheets: false,
21740 // private properties
21741 validationEvent : false,
21743 initialized : false,
21745 sourceEditMode : false,
21746 onFocus : Roo.emptyFn,
21748 hideMode:'offsets',
21752 // blacklist + whitelisted elements..
21759 * Protected method that will not generally be called directly. It
21760 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21761 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21763 getDocMarkup : function(){
21767 // inherit styels from page...??
21768 if (this.stylesheets === false) {
21770 Roo.get(document.head).select('style').each(function(node) {
21771 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21774 Roo.get(document.head).select('link').each(function(node) {
21775 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21778 } else if (!this.stylesheets.length) {
21780 st = '<style type="text/css">' +
21781 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21784 st = '<style type="text/css">' +
21789 st += '<style type="text/css">' +
21790 'IMG { cursor: pointer } ' +
21793 var cls = 'roo-htmleditor-body';
21795 if(this.bodyCls.length){
21796 cls += ' ' + this.bodyCls;
21799 return '<html><head>' + st +
21800 //<style type="text/css">' +
21801 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21803 ' </head><body class="' + cls + '"></body></html>';
21807 onRender : function(ct, position)
21810 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21811 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21814 this.el.dom.style.border = '0 none';
21815 this.el.dom.setAttribute('tabIndex', -1);
21816 this.el.addClass('x-hidden hide');
21820 if(Roo.isIE){ // fix IE 1px bogus margin
21821 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21825 this.frameId = Roo.id();
21829 var iframe = this.owner.wrap.createChild({
21831 cls: 'form-control', // bootstrap..
21833 name: this.frameId,
21834 frameBorder : 'no',
21835 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21840 this.iframe = iframe.dom;
21842 this.assignDocWin();
21844 this.doc.designMode = 'on';
21847 this.doc.write(this.getDocMarkup());
21851 var task = { // must defer to wait for browser to be ready
21853 //console.log("run task?" + this.doc.readyState);
21854 this.assignDocWin();
21855 if(this.doc.body || this.doc.readyState == 'complete'){
21857 this.doc.designMode="on";
21861 Roo.TaskMgr.stop(task);
21862 this.initEditor.defer(10, this);
21869 Roo.TaskMgr.start(task);
21874 onResize : function(w, h)
21876 Roo.log('resize: ' +w + ',' + h );
21877 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21881 if(typeof w == 'number'){
21883 this.iframe.style.width = w + 'px';
21885 if(typeof h == 'number'){
21887 this.iframe.style.height = h + 'px';
21889 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21896 * Toggles the editor between standard and source edit mode.
21897 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21899 toggleSourceEdit : function(sourceEditMode){
21901 this.sourceEditMode = sourceEditMode === true;
21903 if(this.sourceEditMode){
21905 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21908 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21909 //this.iframe.className = '';
21912 //this.setSize(this.owner.wrap.getSize());
21913 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21920 * Protected method that will not generally be called directly. If you need/want
21921 * custom HTML cleanup, this is the method you should override.
21922 * @param {String} html The HTML to be cleaned
21923 * return {String} The cleaned HTML
21925 cleanHtml : function(html){
21926 html = String(html);
21927 if(html.length > 5){
21928 if(Roo.isSafari){ // strip safari nonsense
21929 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21932 if(html == ' '){
21939 * HTML Editor -> Textarea
21940 * Protected method that will not generally be called directly. Syncs the contents
21941 * of the editor iframe with the textarea.
21943 syncValue : function(){
21944 if(this.initialized){
21945 var bd = (this.doc.body || this.doc.documentElement);
21946 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21947 var html = bd.innerHTML;
21949 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21950 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21952 html = '<div style="'+m[0]+'">' + html + '</div>';
21955 html = this.cleanHtml(html);
21956 // fix up the special chars.. normaly like back quotes in word...
21957 // however we do not want to do this with chinese..
21958 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21959 var cc = b.charCodeAt();
21961 (cc >= 0x4E00 && cc < 0xA000 ) ||
21962 (cc >= 0x3400 && cc < 0x4E00 ) ||
21963 (cc >= 0xf900 && cc < 0xfb00 )
21969 if(this.owner.fireEvent('beforesync', this, html) !== false){
21970 this.el.dom.value = html;
21971 this.owner.fireEvent('sync', this, html);
21977 * Protected method that will not generally be called directly. Pushes the value of the textarea
21978 * into the iframe editor.
21980 pushValue : function(){
21981 if(this.initialized){
21982 var v = this.el.dom.value.trim();
21984 // if(v.length < 1){
21988 if(this.owner.fireEvent('beforepush', this, v) !== false){
21989 var d = (this.doc.body || this.doc.documentElement);
21991 this.cleanUpPaste();
21992 this.el.dom.value = d.innerHTML;
21993 this.owner.fireEvent('push', this, v);
21999 deferFocus : function(){
22000 this.focus.defer(10, this);
22004 focus : function(){
22005 if(this.win && !this.sourceEditMode){
22012 assignDocWin: function()
22014 var iframe = this.iframe;
22017 this.doc = iframe.contentWindow.document;
22018 this.win = iframe.contentWindow;
22020 // if (!Roo.get(this.frameId)) {
22023 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22024 // this.win = Roo.get(this.frameId).dom.contentWindow;
22026 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22030 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22031 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22036 initEditor : function(){
22037 //console.log("INIT EDITOR");
22038 this.assignDocWin();
22042 this.doc.designMode="on";
22044 this.doc.write(this.getDocMarkup());
22047 var dbody = (this.doc.body || this.doc.documentElement);
22048 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22049 // this copies styles from the containing element into thsi one..
22050 // not sure why we need all of this..
22051 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22053 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22054 //ss['background-attachment'] = 'fixed'; // w3c
22055 dbody.bgProperties = 'fixed'; // ie
22056 //Roo.DomHelper.applyStyles(dbody, ss);
22057 Roo.EventManager.on(this.doc, {
22058 //'mousedown': this.onEditorEvent,
22059 'mouseup': this.onEditorEvent,
22060 'dblclick': this.onEditorEvent,
22061 'click': this.onEditorEvent,
22062 'keyup': this.onEditorEvent,
22067 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22069 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22070 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22072 this.initialized = true;
22074 this.owner.fireEvent('initialize', this);
22079 onDestroy : function(){
22085 //for (var i =0; i < this.toolbars.length;i++) {
22086 // // fixme - ask toolbars for heights?
22087 // this.toolbars[i].onDestroy();
22090 //this.wrap.dom.innerHTML = '';
22091 //this.wrap.remove();
22096 onFirstFocus : function(){
22098 this.assignDocWin();
22101 this.activated = true;
22104 if(Roo.isGecko){ // prevent silly gecko errors
22106 var s = this.win.getSelection();
22107 if(!s.focusNode || s.focusNode.nodeType != 3){
22108 var r = s.getRangeAt(0);
22109 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22114 this.execCmd('useCSS', true);
22115 this.execCmd('styleWithCSS', false);
22118 this.owner.fireEvent('activate', this);
22122 adjustFont: function(btn){
22123 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22124 //if(Roo.isSafari){ // safari
22127 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22128 if(Roo.isSafari){ // safari
22129 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22130 v = (v < 10) ? 10 : v;
22131 v = (v > 48) ? 48 : v;
22132 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22137 v = Math.max(1, v+adjust);
22139 this.execCmd('FontSize', v );
22142 onEditorEvent : function(e)
22144 this.owner.fireEvent('editorevent', this, e);
22145 // this.updateToolbar();
22146 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22149 insertTag : function(tg)
22151 // could be a bit smarter... -> wrap the current selected tRoo..
22152 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22154 range = this.createRange(this.getSelection());
22155 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22156 wrappingNode.appendChild(range.extractContents());
22157 range.insertNode(wrappingNode);
22164 this.execCmd("formatblock", tg);
22168 insertText : function(txt)
22172 var range = this.createRange();
22173 range.deleteContents();
22174 //alert(Sender.getAttribute('label'));
22176 range.insertNode(this.doc.createTextNode(txt));
22182 * Executes a Midas editor command on the editor document and performs necessary focus and
22183 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22184 * @param {String} cmd The Midas command
22185 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22187 relayCmd : function(cmd, value){
22189 this.execCmd(cmd, value);
22190 this.owner.fireEvent('editorevent', this);
22191 //this.updateToolbar();
22192 this.owner.deferFocus();
22196 * Executes a Midas editor command directly on the editor document.
22197 * For visual commands, you should use {@link #relayCmd} instead.
22198 * <b>This should only be called after the editor is initialized.</b>
22199 * @param {String} cmd The Midas command
22200 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22202 execCmd : function(cmd, value){
22203 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22210 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22212 * @param {String} text | dom node..
22214 insertAtCursor : function(text)
22217 if(!this.activated){
22223 var r = this.doc.selection.createRange();
22234 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22238 // from jquery ui (MIT licenced)
22240 var win = this.win;
22242 if (win.getSelection && win.getSelection().getRangeAt) {
22243 range = win.getSelection().getRangeAt(0);
22244 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22245 range.insertNode(node);
22246 } else if (win.document.selection && win.document.selection.createRange) {
22247 // no firefox support
22248 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22249 win.document.selection.createRange().pasteHTML(txt);
22251 // no firefox support
22252 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22253 this.execCmd('InsertHTML', txt);
22262 mozKeyPress : function(e){
22264 var c = e.getCharCode(), cmd;
22267 c = String.fromCharCode(c).toLowerCase();
22281 this.cleanUpPaste.defer(100, this);
22289 e.preventDefault();
22297 fixKeys : function(){ // load time branching for fastest keydown performance
22299 return function(e){
22300 var k = e.getKey(), r;
22303 r = this.doc.selection.createRange();
22306 r.pasteHTML('    ');
22313 r = this.doc.selection.createRange();
22315 var target = r.parentElement();
22316 if(!target || target.tagName.toLowerCase() != 'li'){
22318 r.pasteHTML('<br />');
22324 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22325 this.cleanUpPaste.defer(100, this);
22331 }else if(Roo.isOpera){
22332 return function(e){
22333 var k = e.getKey();
22337 this.execCmd('InsertHTML','    ');
22340 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22341 this.cleanUpPaste.defer(100, this);
22346 }else if(Roo.isSafari){
22347 return function(e){
22348 var k = e.getKey();
22352 this.execCmd('InsertText','\t');
22356 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22357 this.cleanUpPaste.defer(100, this);
22365 getAllAncestors: function()
22367 var p = this.getSelectedNode();
22370 a.push(p); // push blank onto stack..
22371 p = this.getParentElement();
22375 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22379 a.push(this.doc.body);
22383 lastSelNode : false,
22386 getSelection : function()
22388 this.assignDocWin();
22389 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22392 getSelectedNode: function()
22394 // this may only work on Gecko!!!
22396 // should we cache this!!!!
22401 var range = this.createRange(this.getSelection()).cloneRange();
22404 var parent = range.parentElement();
22406 var testRange = range.duplicate();
22407 testRange.moveToElementText(parent);
22408 if (testRange.inRange(range)) {
22411 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22414 parent = parent.parentElement;
22419 // is ancestor a text element.
22420 var ac = range.commonAncestorContainer;
22421 if (ac.nodeType == 3) {
22422 ac = ac.parentNode;
22425 var ar = ac.childNodes;
22428 var other_nodes = [];
22429 var has_other_nodes = false;
22430 for (var i=0;i<ar.length;i++) {
22431 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22434 // fullly contained node.
22436 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22441 // probably selected..
22442 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22443 other_nodes.push(ar[i]);
22447 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22452 has_other_nodes = true;
22454 if (!nodes.length && other_nodes.length) {
22455 nodes= other_nodes;
22457 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22463 createRange: function(sel)
22465 // this has strange effects when using with
22466 // top toolbar - not sure if it's a great idea.
22467 //this.editor.contentWindow.focus();
22468 if (typeof sel != "undefined") {
22470 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22472 return this.doc.createRange();
22475 return this.doc.createRange();
22478 getParentElement: function()
22481 this.assignDocWin();
22482 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22484 var range = this.createRange(sel);
22487 var p = range.commonAncestorContainer;
22488 while (p.nodeType == 3) { // text node
22499 * Range intersection.. the hard stuff...
22503 * [ -- selected range --- ]
22507 * if end is before start or hits it. fail.
22508 * if start is after end or hits it fail.
22510 * if either hits (but other is outside. - then it's not
22516 // @see http://www.thismuchiknow.co.uk/?p=64.
22517 rangeIntersectsNode : function(range, node)
22519 var nodeRange = node.ownerDocument.createRange();
22521 nodeRange.selectNode(node);
22523 nodeRange.selectNodeContents(node);
22526 var rangeStartRange = range.cloneRange();
22527 rangeStartRange.collapse(true);
22529 var rangeEndRange = range.cloneRange();
22530 rangeEndRange.collapse(false);
22532 var nodeStartRange = nodeRange.cloneRange();
22533 nodeStartRange.collapse(true);
22535 var nodeEndRange = nodeRange.cloneRange();
22536 nodeEndRange.collapse(false);
22538 return rangeStartRange.compareBoundaryPoints(
22539 Range.START_TO_START, nodeEndRange) == -1 &&
22540 rangeEndRange.compareBoundaryPoints(
22541 Range.START_TO_START, nodeStartRange) == 1;
22545 rangeCompareNode : function(range, node)
22547 var nodeRange = node.ownerDocument.createRange();
22549 nodeRange.selectNode(node);
22551 nodeRange.selectNodeContents(node);
22555 range.collapse(true);
22557 nodeRange.collapse(true);
22559 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22560 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22562 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22564 var nodeIsBefore = ss == 1;
22565 var nodeIsAfter = ee == -1;
22567 if (nodeIsBefore && nodeIsAfter) {
22570 if (!nodeIsBefore && nodeIsAfter) {
22571 return 1; //right trailed.
22574 if (nodeIsBefore && !nodeIsAfter) {
22575 return 2; // left trailed.
22581 // private? - in a new class?
22582 cleanUpPaste : function()
22584 // cleans up the whole document..
22585 Roo.log('cleanuppaste');
22587 this.cleanUpChildren(this.doc.body);
22588 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22589 if (clean != this.doc.body.innerHTML) {
22590 this.doc.body.innerHTML = clean;
22595 cleanWordChars : function(input) {// change the chars to hex code
22596 var he = Roo.HtmlEditorCore;
22598 var output = input;
22599 Roo.each(he.swapCodes, function(sw) {
22600 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22602 output = output.replace(swapper, sw[1]);
22609 cleanUpChildren : function (n)
22611 if (!n.childNodes.length) {
22614 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22615 this.cleanUpChild(n.childNodes[i]);
22622 cleanUpChild : function (node)
22625 //console.log(node);
22626 if (node.nodeName == "#text") {
22627 // clean up silly Windows -- stuff?
22630 if (node.nodeName == "#comment") {
22631 node.parentNode.removeChild(node);
22632 // clean up silly Windows -- stuff?
22635 var lcname = node.tagName.toLowerCase();
22636 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22637 // whitelist of tags..
22639 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22641 node.parentNode.removeChild(node);
22646 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22648 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22649 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22651 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22652 // remove_keep_children = true;
22655 if (remove_keep_children) {
22656 this.cleanUpChildren(node);
22657 // inserts everything just before this node...
22658 while (node.childNodes.length) {
22659 var cn = node.childNodes[0];
22660 node.removeChild(cn);
22661 node.parentNode.insertBefore(cn, node);
22663 node.parentNode.removeChild(node);
22667 if (!node.attributes || !node.attributes.length) {
22668 this.cleanUpChildren(node);
22672 function cleanAttr(n,v)
22675 if (v.match(/^\./) || v.match(/^\//)) {
22678 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22681 if (v.match(/^#/)) {
22684 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22685 node.removeAttribute(n);
22689 var cwhite = this.cwhite;
22690 var cblack = this.cblack;
22692 function cleanStyle(n,v)
22694 if (v.match(/expression/)) { //XSS?? should we even bother..
22695 node.removeAttribute(n);
22699 var parts = v.split(/;/);
22702 Roo.each(parts, function(p) {
22703 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22707 var l = p.split(':').shift().replace(/\s+/g,'');
22708 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22710 if ( cwhite.length && cblack.indexOf(l) > -1) {
22711 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22712 //node.removeAttribute(n);
22716 // only allow 'c whitelisted system attributes'
22717 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22718 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22719 //node.removeAttribute(n);
22729 if (clean.length) {
22730 node.setAttribute(n, clean.join(';'));
22732 node.removeAttribute(n);
22738 for (var i = node.attributes.length-1; i > -1 ; i--) {
22739 var a = node.attributes[i];
22742 if (a.name.toLowerCase().substr(0,2)=='on') {
22743 node.removeAttribute(a.name);
22746 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22747 node.removeAttribute(a.name);
22750 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22751 cleanAttr(a.name,a.value); // fixme..
22754 if (a.name == 'style') {
22755 cleanStyle(a.name,a.value);
22758 /// clean up MS crap..
22759 // tecnically this should be a list of valid class'es..
22762 if (a.name == 'class') {
22763 if (a.value.match(/^Mso/)) {
22764 node.className = '';
22767 if (a.value.match(/^body$/)) {
22768 node.className = '';
22779 this.cleanUpChildren(node);
22785 * Clean up MS wordisms...
22787 cleanWord : function(node)
22792 this.cleanWord(this.doc.body);
22795 if (node.nodeName == "#text") {
22796 // clean up silly Windows -- stuff?
22799 if (node.nodeName == "#comment") {
22800 node.parentNode.removeChild(node);
22801 // clean up silly Windows -- stuff?
22805 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22806 node.parentNode.removeChild(node);
22810 // remove - but keep children..
22811 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22812 while (node.childNodes.length) {
22813 var cn = node.childNodes[0];
22814 node.removeChild(cn);
22815 node.parentNode.insertBefore(cn, node);
22817 node.parentNode.removeChild(node);
22818 this.iterateChildren(node, this.cleanWord);
22822 if (node.className.length) {
22824 var cn = node.className.split(/\W+/);
22826 Roo.each(cn, function(cls) {
22827 if (cls.match(/Mso[a-zA-Z]+/)) {
22832 node.className = cna.length ? cna.join(' ') : '';
22834 node.removeAttribute("class");
22838 if (node.hasAttribute("lang")) {
22839 node.removeAttribute("lang");
22842 if (node.hasAttribute("style")) {
22844 var styles = node.getAttribute("style").split(";");
22846 Roo.each(styles, function(s) {
22847 if (!s.match(/:/)) {
22850 var kv = s.split(":");
22851 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22854 // what ever is left... we allow.
22857 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22858 if (!nstyle.length) {
22859 node.removeAttribute('style');
22862 this.iterateChildren(node, this.cleanWord);
22868 * iterateChildren of a Node, calling fn each time, using this as the scole..
22869 * @param {DomNode} node node to iterate children of.
22870 * @param {Function} fn method of this class to call on each item.
22872 iterateChildren : function(node, fn)
22874 if (!node.childNodes.length) {
22877 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22878 fn.call(this, node.childNodes[i])
22884 * cleanTableWidths.
22886 * Quite often pasting from word etc.. results in tables with column and widths.
22887 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22890 cleanTableWidths : function(node)
22895 this.cleanTableWidths(this.doc.body);
22900 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22903 Roo.log(node.tagName);
22904 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22905 this.iterateChildren(node, this.cleanTableWidths);
22908 if (node.hasAttribute('width')) {
22909 node.removeAttribute('width');
22913 if (node.hasAttribute("style")) {
22916 var styles = node.getAttribute("style").split(";");
22918 Roo.each(styles, function(s) {
22919 if (!s.match(/:/)) {
22922 var kv = s.split(":");
22923 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22926 // what ever is left... we allow.
22929 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22930 if (!nstyle.length) {
22931 node.removeAttribute('style');
22935 this.iterateChildren(node, this.cleanTableWidths);
22943 domToHTML : function(currentElement, depth, nopadtext) {
22945 depth = depth || 0;
22946 nopadtext = nopadtext || false;
22948 if (!currentElement) {
22949 return this.domToHTML(this.doc.body);
22952 //Roo.log(currentElement);
22954 var allText = false;
22955 var nodeName = currentElement.nodeName;
22956 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22958 if (nodeName == '#text') {
22960 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22965 if (nodeName != 'BODY') {
22968 // Prints the node tagName, such as <A>, <IMG>, etc
22971 for(i = 0; i < currentElement.attributes.length;i++) {
22973 var aname = currentElement.attributes.item(i).name;
22974 if (!currentElement.attributes.item(i).value.length) {
22977 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22980 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22989 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22992 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22997 // Traverse the tree
22999 var currentElementChild = currentElement.childNodes.item(i);
23000 var allText = true;
23001 var innerHTML = '';
23003 while (currentElementChild) {
23004 // Formatting code (indent the tree so it looks nice on the screen)
23005 var nopad = nopadtext;
23006 if (lastnode == 'SPAN') {
23010 if (currentElementChild.nodeName == '#text') {
23011 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23012 toadd = nopadtext ? toadd : toadd.trim();
23013 if (!nopad && toadd.length > 80) {
23014 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23016 innerHTML += toadd;
23019 currentElementChild = currentElement.childNodes.item(i);
23025 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23027 // Recursively traverse the tree structure of the child node
23028 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23029 lastnode = currentElementChild.nodeName;
23031 currentElementChild=currentElement.childNodes.item(i);
23037 // The remaining code is mostly for formatting the tree
23038 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23043 ret+= "</"+tagName+">";
23049 applyBlacklists : function()
23051 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23052 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23056 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23057 if (b.indexOf(tag) > -1) {
23060 this.white.push(tag);
23064 Roo.each(w, function(tag) {
23065 if (b.indexOf(tag) > -1) {
23068 if (this.white.indexOf(tag) > -1) {
23071 this.white.push(tag);
23076 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23077 if (w.indexOf(tag) > -1) {
23080 this.black.push(tag);
23084 Roo.each(b, function(tag) {
23085 if (w.indexOf(tag) > -1) {
23088 if (this.black.indexOf(tag) > -1) {
23091 this.black.push(tag);
23096 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23097 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23101 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23102 if (b.indexOf(tag) > -1) {
23105 this.cwhite.push(tag);
23109 Roo.each(w, function(tag) {
23110 if (b.indexOf(tag) > -1) {
23113 if (this.cwhite.indexOf(tag) > -1) {
23116 this.cwhite.push(tag);
23121 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23122 if (w.indexOf(tag) > -1) {
23125 this.cblack.push(tag);
23129 Roo.each(b, function(tag) {
23130 if (w.indexOf(tag) > -1) {
23133 if (this.cblack.indexOf(tag) > -1) {
23136 this.cblack.push(tag);
23141 setStylesheets : function(stylesheets)
23143 if(typeof(stylesheets) == 'string'){
23144 Roo.get(this.iframe.contentDocument.head).createChild({
23146 rel : 'stylesheet',
23155 Roo.each(stylesheets, function(s) {
23160 Roo.get(_this.iframe.contentDocument.head).createChild({
23162 rel : 'stylesheet',
23171 removeStylesheets : function()
23175 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23180 setStyle : function(style)
23182 Roo.get(this.iframe.contentDocument.head).createChild({
23191 // hide stuff that is not compatible
23205 * @event specialkey
23209 * @cfg {String} fieldClass @hide
23212 * @cfg {String} focusClass @hide
23215 * @cfg {String} autoCreate @hide
23218 * @cfg {String} inputType @hide
23221 * @cfg {String} invalidClass @hide
23224 * @cfg {String} invalidText @hide
23227 * @cfg {String} msgFx @hide
23230 * @cfg {String} validateOnBlur @hide
23234 Roo.HtmlEditorCore.white = [
23235 'area', 'br', 'img', 'input', 'hr', 'wbr',
23237 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23238 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23239 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23240 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23241 'table', 'ul', 'xmp',
23243 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23246 'dir', 'menu', 'ol', 'ul', 'dl',
23252 Roo.HtmlEditorCore.black = [
23253 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23255 'base', 'basefont', 'bgsound', 'blink', 'body',
23256 'frame', 'frameset', 'head', 'html', 'ilayer',
23257 'iframe', 'layer', 'link', 'meta', 'object',
23258 'script', 'style' ,'title', 'xml' // clean later..
23260 Roo.HtmlEditorCore.clean = [
23261 'script', 'style', 'title', 'xml'
23263 Roo.HtmlEditorCore.remove = [
23268 Roo.HtmlEditorCore.ablack = [
23272 Roo.HtmlEditorCore.aclean = [
23273 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23277 Roo.HtmlEditorCore.pwhite= [
23278 'http', 'https', 'mailto'
23281 // white listed style attributes.
23282 Roo.HtmlEditorCore.cwhite= [
23283 // 'text-align', /// default is to allow most things..
23289 // black listed style attributes.
23290 Roo.HtmlEditorCore.cblack= [
23291 // 'font-size' -- this can be set by the project
23295 Roo.HtmlEditorCore.swapCodes =[
23314 * @class Roo.bootstrap.HtmlEditor
23315 * @extends Roo.bootstrap.TextArea
23316 * Bootstrap HtmlEditor class
23319 * Create a new HtmlEditor
23320 * @param {Object} config The config object
23323 Roo.bootstrap.HtmlEditor = function(config){
23324 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23325 if (!this.toolbars) {
23326 this.toolbars = [];
23329 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23332 * @event initialize
23333 * Fires when the editor is fully initialized (including the iframe)
23334 * @param {HtmlEditor} this
23339 * Fires when the editor is first receives the focus. Any insertion must wait
23340 * until after this event.
23341 * @param {HtmlEditor} this
23345 * @event beforesync
23346 * Fires before the textarea is updated with content from the editor iframe. Return false
23347 * to cancel the sync.
23348 * @param {HtmlEditor} this
23349 * @param {String} html
23353 * @event beforepush
23354 * Fires before the iframe editor is updated with content from the textarea. Return false
23355 * to cancel the push.
23356 * @param {HtmlEditor} this
23357 * @param {String} html
23362 * Fires when the textarea is updated with content from the editor iframe.
23363 * @param {HtmlEditor} this
23364 * @param {String} html
23369 * Fires when the iframe editor is updated with content from the textarea.
23370 * @param {HtmlEditor} this
23371 * @param {String} html
23375 * @event editmodechange
23376 * Fires when the editor switches edit modes
23377 * @param {HtmlEditor} this
23378 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23380 editmodechange: true,
23382 * @event editorevent
23383 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23384 * @param {HtmlEditor} this
23388 * @event firstfocus
23389 * Fires when on first focus - needed by toolbars..
23390 * @param {HtmlEditor} this
23395 * Auto save the htmlEditor value as a file into Events
23396 * @param {HtmlEditor} this
23400 * @event savedpreview
23401 * preview the saved version of htmlEditor
23402 * @param {HtmlEditor} this
23409 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23413 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23418 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23423 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23428 * @cfg {Number} height (in pixels)
23432 * @cfg {Number} width (in pixels)
23437 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23440 stylesheets: false,
23445 // private properties
23446 validationEvent : false,
23448 initialized : false,
23451 onFocus : Roo.emptyFn,
23453 hideMode:'offsets',
23455 tbContainer : false,
23459 toolbarContainer :function() {
23460 return this.wrap.select('.x-html-editor-tb',true).first();
23464 * Protected method that will not generally be called directly. It
23465 * is called when the editor creates its toolbar. Override this method if you need to
23466 * add custom toolbar buttons.
23467 * @param {HtmlEditor} editor
23469 createToolbar : function(){
23470 Roo.log('renewing');
23471 Roo.log("create toolbars");
23473 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23474 this.toolbars[0].render(this.toolbarContainer());
23478 // if (!editor.toolbars || !editor.toolbars.length) {
23479 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23482 // for (var i =0 ; i < editor.toolbars.length;i++) {
23483 // editor.toolbars[i] = Roo.factory(
23484 // typeof(editor.toolbars[i]) == 'string' ?
23485 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23486 // Roo.bootstrap.HtmlEditor);
23487 // editor.toolbars[i].init(editor);
23493 onRender : function(ct, position)
23495 // Roo.log("Call onRender: " + this.xtype);
23497 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23499 this.wrap = this.inputEl().wrap({
23500 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23503 this.editorcore.onRender(ct, position);
23505 if (this.resizable) {
23506 this.resizeEl = new Roo.Resizable(this.wrap, {
23510 minHeight : this.height,
23511 height: this.height,
23512 handles : this.resizable,
23515 resize : function(r, w, h) {
23516 _t.onResize(w,h); // -something
23522 this.createToolbar(this);
23525 if(!this.width && this.resizable){
23526 this.setSize(this.wrap.getSize());
23528 if (this.resizeEl) {
23529 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23530 // should trigger onReize..
23536 onResize : function(w, h)
23538 Roo.log('resize: ' +w + ',' + h );
23539 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23543 if(this.inputEl() ){
23544 if(typeof w == 'number'){
23545 var aw = w - this.wrap.getFrameWidth('lr');
23546 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23549 if(typeof h == 'number'){
23550 var tbh = -11; // fixme it needs to tool bar size!
23551 for (var i =0; i < this.toolbars.length;i++) {
23552 // fixme - ask toolbars for heights?
23553 tbh += this.toolbars[i].el.getHeight();
23554 //if (this.toolbars[i].footer) {
23555 // tbh += this.toolbars[i].footer.el.getHeight();
23563 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23564 ah -= 5; // knock a few pixes off for look..
23565 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23569 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23570 this.editorcore.onResize(ew,eh);
23575 * Toggles the editor between standard and source edit mode.
23576 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23578 toggleSourceEdit : function(sourceEditMode)
23580 this.editorcore.toggleSourceEdit(sourceEditMode);
23582 if(this.editorcore.sourceEditMode){
23583 Roo.log('editor - showing textarea');
23586 // Roo.log(this.syncValue());
23588 this.inputEl().removeClass(['hide', 'x-hidden']);
23589 this.inputEl().dom.removeAttribute('tabIndex');
23590 this.inputEl().focus();
23592 Roo.log('editor - hiding textarea');
23594 // Roo.log(this.pushValue());
23597 this.inputEl().addClass(['hide', 'x-hidden']);
23598 this.inputEl().dom.setAttribute('tabIndex', -1);
23599 //this.deferFocus();
23602 if(this.resizable){
23603 this.setSize(this.wrap.getSize());
23606 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23609 // private (for BoxComponent)
23610 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23612 // private (for BoxComponent)
23613 getResizeEl : function(){
23617 // private (for BoxComponent)
23618 getPositionEl : function(){
23623 initEvents : function(){
23624 this.originalValue = this.getValue();
23628 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23631 // markInvalid : Roo.emptyFn,
23633 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23636 // clearInvalid : Roo.emptyFn,
23638 setValue : function(v){
23639 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23640 this.editorcore.pushValue();
23645 deferFocus : function(){
23646 this.focus.defer(10, this);
23650 focus : function(){
23651 this.editorcore.focus();
23657 onDestroy : function(){
23663 for (var i =0; i < this.toolbars.length;i++) {
23664 // fixme - ask toolbars for heights?
23665 this.toolbars[i].onDestroy();
23668 this.wrap.dom.innerHTML = '';
23669 this.wrap.remove();
23674 onFirstFocus : function(){
23675 //Roo.log("onFirstFocus");
23676 this.editorcore.onFirstFocus();
23677 for (var i =0; i < this.toolbars.length;i++) {
23678 this.toolbars[i].onFirstFocus();
23684 syncValue : function()
23686 this.editorcore.syncValue();
23689 pushValue : function()
23691 this.editorcore.pushValue();
23695 // hide stuff that is not compatible
23709 * @event specialkey
23713 * @cfg {String} fieldClass @hide
23716 * @cfg {String} focusClass @hide
23719 * @cfg {String} autoCreate @hide
23722 * @cfg {String} inputType @hide
23725 * @cfg {String} invalidClass @hide
23728 * @cfg {String} invalidText @hide
23731 * @cfg {String} msgFx @hide
23734 * @cfg {String} validateOnBlur @hide
23743 Roo.namespace('Roo.bootstrap.htmleditor');
23745 * @class Roo.bootstrap.HtmlEditorToolbar1
23750 new Roo.bootstrap.HtmlEditor({
23753 new Roo.bootstrap.HtmlEditorToolbar1({
23754 disable : { fonts: 1 , format: 1, ..., ... , ...],
23760 * @cfg {Object} disable List of elements to disable..
23761 * @cfg {Array} btns List of additional buttons.
23765 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23768 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23771 Roo.apply(this, config);
23773 // default disabled, based on 'good practice'..
23774 this.disable = this.disable || {};
23775 Roo.applyIf(this.disable, {
23778 specialElements : true
23780 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23782 this.editor = config.editor;
23783 this.editorcore = config.editor.editorcore;
23785 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23787 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23788 // dont call parent... till later.
23790 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23795 editorcore : false,
23800 "h1","h2","h3","h4","h5","h6",
23802 "abbr", "acronym", "address", "cite", "samp", "var",
23806 onRender : function(ct, position)
23808 // Roo.log("Call onRender: " + this.xtype);
23810 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23812 this.el.dom.style.marginBottom = '0';
23814 var editorcore = this.editorcore;
23815 var editor= this.editor;
23818 var btn = function(id,cmd , toggle, handler, html){
23820 var event = toggle ? 'toggle' : 'click';
23825 xns: Roo.bootstrap,
23828 enableToggle:toggle !== false,
23830 pressed : toggle ? false : null,
23833 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23834 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23840 // var cb_box = function...
23845 xns: Roo.bootstrap,
23846 glyphicon : 'font',
23850 xns: Roo.bootstrap,
23854 Roo.each(this.formats, function(f) {
23855 style.menu.items.push({
23857 xns: Roo.bootstrap,
23858 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23863 editorcore.insertTag(this.tagname);
23870 children.push(style);
23872 btn('bold',false,true);
23873 btn('italic',false,true);
23874 btn('align-left', 'justifyleft',true);
23875 btn('align-center', 'justifycenter',true);
23876 btn('align-right' , 'justifyright',true);
23877 btn('link', false, false, function(btn) {
23878 //Roo.log("create link?");
23879 var url = prompt(this.createLinkText, this.defaultLinkValue);
23880 if(url && url != 'http:/'+'/'){
23881 this.editorcore.relayCmd('createlink', url);
23884 btn('list','insertunorderedlist',true);
23885 btn('pencil', false,true, function(btn){
23887 this.toggleSourceEdit(btn.pressed);
23890 if (this.editor.btns.length > 0) {
23891 for (var i = 0; i<this.editor.btns.length; i++) {
23892 children.push(this.editor.btns[i]);
23900 xns: Roo.bootstrap,
23905 xns: Roo.bootstrap,
23910 cog.menu.items.push({
23912 xns: Roo.bootstrap,
23913 html : Clean styles,
23918 editorcore.insertTag(this.tagname);
23927 this.xtype = 'NavSimplebar';
23929 for(var i=0;i< children.length;i++) {
23931 this.buttons.add(this.addxtypeChild(children[i]));
23935 editor.on('editorevent', this.updateToolbar, this);
23937 onBtnClick : function(id)
23939 this.editorcore.relayCmd(id);
23940 this.editorcore.focus();
23944 * Protected method that will not generally be called directly. It triggers
23945 * a toolbar update by reading the markup state of the current selection in the editor.
23947 updateToolbar: function(){
23949 if(!this.editorcore.activated){
23950 this.editor.onFirstFocus(); // is this neeed?
23954 var btns = this.buttons;
23955 var doc = this.editorcore.doc;
23956 btns.get('bold').setActive(doc.queryCommandState('bold'));
23957 btns.get('italic').setActive(doc.queryCommandState('italic'));
23958 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23960 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23961 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23962 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23964 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23965 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23968 var ans = this.editorcore.getAllAncestors();
23969 if (this.formatCombo) {
23972 var store = this.formatCombo.store;
23973 this.formatCombo.setValue("");
23974 for (var i =0; i < ans.length;i++) {
23975 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23977 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23985 // hides menus... - so this cant be on a menu...
23986 Roo.bootstrap.MenuMgr.hideAll();
23988 Roo.bootstrap.MenuMgr.hideAll();
23989 //this.editorsyncValue();
23991 onFirstFocus: function() {
23992 this.buttons.each(function(item){
23996 toggleSourceEdit : function(sourceEditMode){
23999 if(sourceEditMode){
24000 Roo.log("disabling buttons");
24001 this.buttons.each( function(item){
24002 if(item.cmd != 'pencil'){
24008 Roo.log("enabling buttons");
24009 if(this.editorcore.initialized){
24010 this.buttons.each( function(item){
24016 Roo.log("calling toggole on editor");
24017 // tell the editor that it's been pressed..
24018 this.editor.toggleSourceEdit(sourceEditMode);
24028 * @class Roo.bootstrap.Table.AbstractSelectionModel
24029 * @extends Roo.util.Observable
24030 * Abstract base class for grid SelectionModels. It provides the interface that should be
24031 * implemented by descendant classes. This class should not be directly instantiated.
24034 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24035 this.locked = false;
24036 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24040 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24041 /** @ignore Called by the grid automatically. Do not call directly. */
24042 init : function(grid){
24048 * Locks the selections.
24051 this.locked = true;
24055 * Unlocks the selections.
24057 unlock : function(){
24058 this.locked = false;
24062 * Returns true if the selections are locked.
24063 * @return {Boolean}
24065 isLocked : function(){
24066 return this.locked;
24070 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24071 * @class Roo.bootstrap.Table.RowSelectionModel
24072 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24073 * It supports multiple selections and keyboard selection/navigation.
24075 * @param {Object} config
24078 Roo.bootstrap.Table.RowSelectionModel = function(config){
24079 Roo.apply(this, config);
24080 this.selections = new Roo.util.MixedCollection(false, function(o){
24085 this.lastActive = false;
24089 * @event selectionchange
24090 * Fires when the selection changes
24091 * @param {SelectionModel} this
24093 "selectionchange" : true,
24095 * @event afterselectionchange
24096 * Fires after the selection changes (eg. by key press or clicking)
24097 * @param {SelectionModel} this
24099 "afterselectionchange" : true,
24101 * @event beforerowselect
24102 * Fires when a row is selected being selected, return false to cancel.
24103 * @param {SelectionModel} this
24104 * @param {Number} rowIndex The selected index
24105 * @param {Boolean} keepExisting False if other selections will be cleared
24107 "beforerowselect" : true,
24110 * Fires when a row is selected.
24111 * @param {SelectionModel} this
24112 * @param {Number} rowIndex The selected index
24113 * @param {Roo.data.Record} r The record
24115 "rowselect" : true,
24117 * @event rowdeselect
24118 * Fires when a row is deselected.
24119 * @param {SelectionModel} this
24120 * @param {Number} rowIndex The selected index
24122 "rowdeselect" : true
24124 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24125 this.locked = false;
24128 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24130 * @cfg {Boolean} singleSelect
24131 * True to allow selection of only one row at a time (defaults to false)
24133 singleSelect : false,
24136 initEvents : function()
24139 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24140 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24141 //}else{ // allow click to work like normal
24142 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24144 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24145 this.grid.on("rowclick", this.handleMouseDown, this);
24147 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24148 "up" : function(e){
24150 this.selectPrevious(e.shiftKey);
24151 }else if(this.last !== false && this.lastActive !== false){
24152 var last = this.last;
24153 this.selectRange(this.last, this.lastActive-1);
24154 this.grid.getView().focusRow(this.lastActive);
24155 if(last !== false){
24159 this.selectFirstRow();
24161 this.fireEvent("afterselectionchange", this);
24163 "down" : function(e){
24165 this.selectNext(e.shiftKey);
24166 }else if(this.last !== false && this.lastActive !== false){
24167 var last = this.last;
24168 this.selectRange(this.last, this.lastActive+1);
24169 this.grid.getView().focusRow(this.lastActive);
24170 if(last !== false){
24174 this.selectFirstRow();
24176 this.fireEvent("afterselectionchange", this);
24180 this.grid.store.on('load', function(){
24181 this.selections.clear();
24184 var view = this.grid.view;
24185 view.on("refresh", this.onRefresh, this);
24186 view.on("rowupdated", this.onRowUpdated, this);
24187 view.on("rowremoved", this.onRemove, this);
24192 onRefresh : function()
24194 var ds = this.grid.store, i, v = this.grid.view;
24195 var s = this.selections;
24196 s.each(function(r){
24197 if((i = ds.indexOfId(r.id)) != -1){
24206 onRemove : function(v, index, r){
24207 this.selections.remove(r);
24211 onRowUpdated : function(v, index, r){
24212 if(this.isSelected(r)){
24213 v.onRowSelect(index);
24219 * @param {Array} records The records to select
24220 * @param {Boolean} keepExisting (optional) True to keep existing selections
24222 selectRecords : function(records, keepExisting)
24225 this.clearSelections();
24227 var ds = this.grid.store;
24228 for(var i = 0, len = records.length; i < len; i++){
24229 this.selectRow(ds.indexOf(records[i]), true);
24234 * Gets the number of selected rows.
24237 getCount : function(){
24238 return this.selections.length;
24242 * Selects the first row in the grid.
24244 selectFirstRow : function(){
24249 * Select the last row.
24250 * @param {Boolean} keepExisting (optional) True to keep existing selections
24252 selectLastRow : function(keepExisting){
24253 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24254 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24258 * Selects the row immediately following the last selected row.
24259 * @param {Boolean} keepExisting (optional) True to keep existing selections
24261 selectNext : function(keepExisting)
24263 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24264 this.selectRow(this.last+1, keepExisting);
24265 this.grid.getView().focusRow(this.last);
24270 * Selects the row that precedes the last selected row.
24271 * @param {Boolean} keepExisting (optional) True to keep existing selections
24273 selectPrevious : function(keepExisting){
24275 this.selectRow(this.last-1, keepExisting);
24276 this.grid.getView().focusRow(this.last);
24281 * Returns the selected records
24282 * @return {Array} Array of selected records
24284 getSelections : function(){
24285 return [].concat(this.selections.items);
24289 * Returns the first selected record.
24292 getSelected : function(){
24293 return this.selections.itemAt(0);
24298 * Clears all selections.
24300 clearSelections : function(fast)
24306 var ds = this.grid.store;
24307 var s = this.selections;
24308 s.each(function(r){
24309 this.deselectRow(ds.indexOfId(r.id));
24313 this.selections.clear();
24320 * Selects all rows.
24322 selectAll : function(){
24326 this.selections.clear();
24327 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24328 this.selectRow(i, true);
24333 * Returns True if there is a selection.
24334 * @return {Boolean}
24336 hasSelection : function(){
24337 return this.selections.length > 0;
24341 * Returns True if the specified row is selected.
24342 * @param {Number/Record} record The record or index of the record to check
24343 * @return {Boolean}
24345 isSelected : function(index){
24346 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24347 return (r && this.selections.key(r.id) ? true : false);
24351 * Returns True if the specified record id is selected.
24352 * @param {String} id The id of record to check
24353 * @return {Boolean}
24355 isIdSelected : function(id){
24356 return (this.selections.key(id) ? true : false);
24361 handleMouseDBClick : function(e, t){
24365 handleMouseDown : function(e, t)
24367 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24368 if(this.isLocked() || rowIndex < 0 ){
24371 if(e.shiftKey && this.last !== false){
24372 var last = this.last;
24373 this.selectRange(last, rowIndex, e.ctrlKey);
24374 this.last = last; // reset the last
24378 var isSelected = this.isSelected(rowIndex);
24379 //Roo.log("select row:" + rowIndex);
24381 this.deselectRow(rowIndex);
24383 this.selectRow(rowIndex, true);
24387 if(e.button !== 0 && isSelected){
24388 alert('rowIndex 2: ' + rowIndex);
24389 view.focusRow(rowIndex);
24390 }else if(e.ctrlKey && isSelected){
24391 this.deselectRow(rowIndex);
24392 }else if(!isSelected){
24393 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24394 view.focusRow(rowIndex);
24398 this.fireEvent("afterselectionchange", this);
24401 handleDragableRowClick : function(grid, rowIndex, e)
24403 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24404 this.selectRow(rowIndex, false);
24405 grid.view.focusRow(rowIndex);
24406 this.fireEvent("afterselectionchange", this);
24411 * Selects multiple rows.
24412 * @param {Array} rows Array of the indexes of the row to select
24413 * @param {Boolean} keepExisting (optional) True to keep existing selections
24415 selectRows : function(rows, keepExisting){
24417 this.clearSelections();
24419 for(var i = 0, len = rows.length; i < len; i++){
24420 this.selectRow(rows[i], true);
24425 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24426 * @param {Number} startRow The index of the first row in the range
24427 * @param {Number} endRow The index of the last row in the range
24428 * @param {Boolean} keepExisting (optional) True to retain existing selections
24430 selectRange : function(startRow, endRow, keepExisting){
24435 this.clearSelections();
24437 if(startRow <= endRow){
24438 for(var i = startRow; i <= endRow; i++){
24439 this.selectRow(i, true);
24442 for(var i = startRow; i >= endRow; i--){
24443 this.selectRow(i, true);
24449 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24450 * @param {Number} startRow The index of the first row in the range
24451 * @param {Number} endRow The index of the last row in the range
24453 deselectRange : function(startRow, endRow, preventViewNotify){
24457 for(var i = startRow; i <= endRow; i++){
24458 this.deselectRow(i, preventViewNotify);
24464 * @param {Number} row The index of the row to select
24465 * @param {Boolean} keepExisting (optional) True to keep existing selections
24467 selectRow : function(index, keepExisting, preventViewNotify)
24469 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24472 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24473 if(!keepExisting || this.singleSelect){
24474 this.clearSelections();
24477 var r = this.grid.store.getAt(index);
24478 //console.log('selectRow - record id :' + r.id);
24480 this.selections.add(r);
24481 this.last = this.lastActive = index;
24482 if(!preventViewNotify){
24483 var proxy = new Roo.Element(
24484 this.grid.getRowDom(index)
24486 proxy.addClass('bg-info info');
24488 this.fireEvent("rowselect", this, index, r);
24489 this.fireEvent("selectionchange", this);
24495 * @param {Number} row The index of the row to deselect
24497 deselectRow : function(index, preventViewNotify)
24502 if(this.last == index){
24505 if(this.lastActive == index){
24506 this.lastActive = false;
24509 var r = this.grid.store.getAt(index);
24514 this.selections.remove(r);
24515 //.console.log('deselectRow - record id :' + r.id);
24516 if(!preventViewNotify){
24518 var proxy = new Roo.Element(
24519 this.grid.getRowDom(index)
24521 proxy.removeClass('bg-info info');
24523 this.fireEvent("rowdeselect", this, index);
24524 this.fireEvent("selectionchange", this);
24528 restoreLast : function(){
24530 this.last = this._last;
24535 acceptsNav : function(row, col, cm){
24536 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24540 onEditorKey : function(field, e){
24541 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24546 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24548 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24550 }else if(k == e.ENTER && !e.ctrlKey){
24554 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24556 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24558 }else if(k == e.ESC){
24562 g.startEditing(newCell[0], newCell[1]);
24568 * Ext JS Library 1.1.1
24569 * Copyright(c) 2006-2007, Ext JS, LLC.
24571 * Originally Released Under LGPL - original licence link has changed is not relivant.
24574 * <script type="text/javascript">
24578 * @class Roo.bootstrap.PagingToolbar
24579 * @extends Roo.bootstrap.NavSimplebar
24580 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24582 * Create a new PagingToolbar
24583 * @param {Object} config The config object
24584 * @param {Roo.data.Store} store
24586 Roo.bootstrap.PagingToolbar = function(config)
24588 // old args format still supported... - xtype is prefered..
24589 // created from xtype...
24591 this.ds = config.dataSource;
24593 if (config.store && !this.ds) {
24594 this.store= Roo.factory(config.store, Roo.data);
24595 this.ds = this.store;
24596 this.ds.xmodule = this.xmodule || false;
24599 this.toolbarItems = [];
24600 if (config.items) {
24601 this.toolbarItems = config.items;
24604 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24609 this.bind(this.ds);
24612 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24616 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24618 * @cfg {Roo.data.Store} dataSource
24619 * The underlying data store providing the paged data
24622 * @cfg {String/HTMLElement/Element} container
24623 * container The id or element that will contain the toolbar
24626 * @cfg {Boolean} displayInfo
24627 * True to display the displayMsg (defaults to false)
24630 * @cfg {Number} pageSize
24631 * The number of records to display per page (defaults to 20)
24635 * @cfg {String} displayMsg
24636 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24638 displayMsg : 'Displaying {0} - {1} of {2}',
24640 * @cfg {String} emptyMsg
24641 * The message to display when no records are found (defaults to "No data to display")
24643 emptyMsg : 'No data to display',
24645 * Customizable piece of the default paging text (defaults to "Page")
24648 beforePageText : "Page",
24650 * Customizable piece of the default paging text (defaults to "of %0")
24653 afterPageText : "of {0}",
24655 * Customizable piece of the default paging text (defaults to "First Page")
24658 firstText : "First Page",
24660 * Customizable piece of the default paging text (defaults to "Previous Page")
24663 prevText : "Previous Page",
24665 * Customizable piece of the default paging text (defaults to "Next Page")
24668 nextText : "Next Page",
24670 * Customizable piece of the default paging text (defaults to "Last Page")
24673 lastText : "Last Page",
24675 * Customizable piece of the default paging text (defaults to "Refresh")
24678 refreshText : "Refresh",
24682 onRender : function(ct, position)
24684 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24685 this.navgroup.parentId = this.id;
24686 this.navgroup.onRender(this.el, null);
24687 // add the buttons to the navgroup
24689 if(this.displayInfo){
24690 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24691 this.displayEl = this.el.select('.x-paging-info', true).first();
24692 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24693 // this.displayEl = navel.el.select('span',true).first();
24699 Roo.each(_this.buttons, function(e){ // this might need to use render????
24700 Roo.factory(e).render(_this.el);
24704 Roo.each(_this.toolbarItems, function(e) {
24705 _this.navgroup.addItem(e);
24709 this.first = this.navgroup.addItem({
24710 tooltip: this.firstText,
24712 icon : 'fa fa-backward',
24714 preventDefault: true,
24715 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24718 this.prev = this.navgroup.addItem({
24719 tooltip: this.prevText,
24721 icon : 'fa fa-step-backward',
24723 preventDefault: true,
24724 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24726 //this.addSeparator();
24729 var field = this.navgroup.addItem( {
24731 cls : 'x-paging-position',
24733 html : this.beforePageText +
24734 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24735 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24738 this.field = field.el.select('input', true).first();
24739 this.field.on("keydown", this.onPagingKeydown, this);
24740 this.field.on("focus", function(){this.dom.select();});
24743 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24744 //this.field.setHeight(18);
24745 //this.addSeparator();
24746 this.next = this.navgroup.addItem({
24747 tooltip: this.nextText,
24749 html : ' <i class="fa fa-step-forward">',
24751 preventDefault: true,
24752 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24754 this.last = this.navgroup.addItem({
24755 tooltip: this.lastText,
24756 icon : 'fa fa-forward',
24759 preventDefault: true,
24760 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24762 //this.addSeparator();
24763 this.loading = this.navgroup.addItem({
24764 tooltip: this.refreshText,
24765 icon: 'fa fa-refresh',
24766 preventDefault: true,
24767 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24773 updateInfo : function(){
24774 if(this.displayEl){
24775 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24776 var msg = count == 0 ?
24780 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24782 this.displayEl.update(msg);
24787 onLoad : function(ds, r, o)
24789 this.cursor = o.params.start ? o.params.start : 0;
24791 var d = this.getPageData(),
24796 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24797 this.field.dom.value = ap;
24798 this.first.setDisabled(ap == 1);
24799 this.prev.setDisabled(ap == 1);
24800 this.next.setDisabled(ap == ps);
24801 this.last.setDisabled(ap == ps);
24802 this.loading.enable();
24807 getPageData : function(){
24808 var total = this.ds.getTotalCount();
24811 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24812 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24817 onLoadError : function(){
24818 this.loading.enable();
24822 onPagingKeydown : function(e){
24823 var k = e.getKey();
24824 var d = this.getPageData();
24826 var v = this.field.dom.value, pageNum;
24827 if(!v || isNaN(pageNum = parseInt(v, 10))){
24828 this.field.dom.value = d.activePage;
24831 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24832 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24835 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))
24837 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24838 this.field.dom.value = pageNum;
24839 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24842 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24844 var v = this.field.dom.value, pageNum;
24845 var increment = (e.shiftKey) ? 10 : 1;
24846 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24849 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24850 this.field.dom.value = d.activePage;
24853 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24855 this.field.dom.value = parseInt(v, 10) + increment;
24856 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24857 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24864 beforeLoad : function(){
24866 this.loading.disable();
24871 onClick : function(which){
24880 ds.load({params:{start: 0, limit: this.pageSize}});
24883 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24886 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24889 var total = ds.getTotalCount();
24890 var extra = total % this.pageSize;
24891 var lastStart = extra ? (total - extra) : total-this.pageSize;
24892 ds.load({params:{start: lastStart, limit: this.pageSize}});
24895 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24901 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24902 * @param {Roo.data.Store} store The data store to unbind
24904 unbind : function(ds){
24905 ds.un("beforeload", this.beforeLoad, this);
24906 ds.un("load", this.onLoad, this);
24907 ds.un("loadexception", this.onLoadError, this);
24908 ds.un("remove", this.updateInfo, this);
24909 ds.un("add", this.updateInfo, this);
24910 this.ds = undefined;
24914 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24915 * @param {Roo.data.Store} store The data store to bind
24917 bind : function(ds){
24918 ds.on("beforeload", this.beforeLoad, this);
24919 ds.on("load", this.onLoad, this);
24920 ds.on("loadexception", this.onLoadError, this);
24921 ds.on("remove", this.updateInfo, this);
24922 ds.on("add", this.updateInfo, this);
24933 * @class Roo.bootstrap.MessageBar
24934 * @extends Roo.bootstrap.Component
24935 * Bootstrap MessageBar class
24936 * @cfg {String} html contents of the MessageBar
24937 * @cfg {String} weight (info | success | warning | danger) default info
24938 * @cfg {String} beforeClass insert the bar before the given class
24939 * @cfg {Boolean} closable (true | false) default false
24940 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24943 * Create a new Element
24944 * @param {Object} config The config object
24947 Roo.bootstrap.MessageBar = function(config){
24948 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24951 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24957 beforeClass: 'bootstrap-sticky-wrap',
24959 getAutoCreate : function(){
24963 cls: 'alert alert-dismissable alert-' + this.weight,
24968 html: this.html || ''
24974 cfg.cls += ' alert-messages-fixed';
24988 onRender : function(ct, position)
24990 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24993 var cfg = Roo.apply({}, this.getAutoCreate());
24997 cfg.cls += ' ' + this.cls;
25000 cfg.style = this.style;
25002 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25004 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25007 this.el.select('>button.close').on('click', this.hide, this);
25013 if (!this.rendered) {
25019 this.fireEvent('show', this);
25025 if (!this.rendered) {
25031 this.fireEvent('hide', this);
25034 update : function()
25036 // var e = this.el.dom.firstChild;
25038 // if(this.closable){
25039 // e = e.nextSibling;
25042 // e.data = this.html || '';
25044 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25060 * @class Roo.bootstrap.Graph
25061 * @extends Roo.bootstrap.Component
25062 * Bootstrap Graph class
25066 @cfg {String} graphtype bar | vbar | pie
25067 @cfg {number} g_x coodinator | centre x (pie)
25068 @cfg {number} g_y coodinator | centre y (pie)
25069 @cfg {number} g_r radius (pie)
25070 @cfg {number} g_height height of the chart (respected by all elements in the set)
25071 @cfg {number} g_width width of the chart (respected by all elements in the set)
25072 @cfg {Object} title The title of the chart
25075 -opts (object) options for the chart
25077 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25078 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25080 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.
25081 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25083 o stretch (boolean)
25085 -opts (object) options for the pie
25088 o startAngle (number)
25089 o endAngle (number)
25093 * Create a new Input
25094 * @param {Object} config The config object
25097 Roo.bootstrap.Graph = function(config){
25098 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25104 * The img click event for the img.
25105 * @param {Roo.EventObject} e
25111 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25122 //g_colors: this.colors,
25129 getAutoCreate : function(){
25140 onRender : function(ct,position){
25143 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25145 if (typeof(Raphael) == 'undefined') {
25146 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25150 this.raphael = Raphael(this.el.dom);
25152 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25153 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25154 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25155 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25157 r.text(160, 10, "Single Series Chart").attr(txtattr);
25158 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25159 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25160 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25162 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25163 r.barchart(330, 10, 300, 220, data1);
25164 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25165 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25168 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25169 // r.barchart(30, 30, 560, 250, xdata, {
25170 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25171 // axis : "0 0 1 1",
25172 // axisxlabels : xdata
25173 // //yvalues : cols,
25176 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25178 // this.load(null,xdata,{
25179 // axis : "0 0 1 1",
25180 // axisxlabels : xdata
25185 load : function(graphtype,xdata,opts)
25187 this.raphael.clear();
25189 graphtype = this.graphtype;
25194 var r = this.raphael,
25195 fin = function () {
25196 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25198 fout = function () {
25199 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25201 pfin = function() {
25202 this.sector.stop();
25203 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25206 this.label[0].stop();
25207 this.label[0].attr({ r: 7.5 });
25208 this.label[1].attr({ "font-weight": 800 });
25211 pfout = function() {
25212 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25215 this.label[0].animate({ r: 5 }, 500, "bounce");
25216 this.label[1].attr({ "font-weight": 400 });
25222 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25225 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25228 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25229 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25231 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25238 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25243 setTitle: function(o)
25248 initEvents: function() {
25251 this.el.on('click', this.onClick, this);
25255 onClick : function(e)
25257 Roo.log('img onclick');
25258 this.fireEvent('click', this, e);
25270 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25273 * @class Roo.bootstrap.dash.NumberBox
25274 * @extends Roo.bootstrap.Component
25275 * Bootstrap NumberBox class
25276 * @cfg {String} headline Box headline
25277 * @cfg {String} content Box content
25278 * @cfg {String} icon Box icon
25279 * @cfg {String} footer Footer text
25280 * @cfg {String} fhref Footer href
25283 * Create a new NumberBox
25284 * @param {Object} config The config object
25288 Roo.bootstrap.dash.NumberBox = function(config){
25289 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25293 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25302 getAutoCreate : function(){
25306 cls : 'small-box ',
25314 cls : 'roo-headline',
25315 html : this.headline
25319 cls : 'roo-content',
25320 html : this.content
25334 cls : 'ion ' + this.icon
25343 cls : 'small-box-footer',
25344 href : this.fhref || '#',
25348 cfg.cn.push(footer);
25355 onRender : function(ct,position){
25356 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25363 setHeadline: function (value)
25365 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25368 setFooter: function (value, href)
25370 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25373 this.el.select('a.small-box-footer',true).first().attr('href', href);
25378 setContent: function (value)
25380 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25383 initEvents: function()
25397 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25400 * @class Roo.bootstrap.dash.TabBox
25401 * @extends Roo.bootstrap.Component
25402 * Bootstrap TabBox class
25403 * @cfg {String} title Title of the TabBox
25404 * @cfg {String} icon Icon of the TabBox
25405 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25406 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25409 * Create a new TabBox
25410 * @param {Object} config The config object
25414 Roo.bootstrap.dash.TabBox = function(config){
25415 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25420 * When a pane is added
25421 * @param {Roo.bootstrap.dash.TabPane} pane
25425 * @event activatepane
25426 * When a pane is activated
25427 * @param {Roo.bootstrap.dash.TabPane} pane
25429 "activatepane" : true
25437 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25442 tabScrollable : false,
25444 getChildContainer : function()
25446 return this.el.select('.tab-content', true).first();
25449 getAutoCreate : function(){
25453 cls: 'pull-left header',
25461 cls: 'fa ' + this.icon
25467 cls: 'nav nav-tabs pull-right',
25473 if(this.tabScrollable){
25480 cls: 'nav nav-tabs pull-right',
25491 cls: 'nav-tabs-custom',
25496 cls: 'tab-content no-padding',
25504 initEvents : function()
25506 //Roo.log('add add pane handler');
25507 this.on('addpane', this.onAddPane, this);
25510 * Updates the box title
25511 * @param {String} html to set the title to.
25513 setTitle : function(value)
25515 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25517 onAddPane : function(pane)
25519 this.panes.push(pane);
25520 //Roo.log('addpane');
25522 // tabs are rendere left to right..
25523 if(!this.showtabs){
25527 var ctr = this.el.select('.nav-tabs', true).first();
25530 var existing = ctr.select('.nav-tab',true);
25531 var qty = existing.getCount();;
25534 var tab = ctr.createChild({
25536 cls : 'nav-tab' + (qty ? '' : ' active'),
25544 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25547 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25549 pane.el.addClass('active');
25554 onTabClick : function(ev,un,ob,pane)
25556 //Roo.log('tab - prev default');
25557 ev.preventDefault();
25560 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25561 pane.tab.addClass('active');
25562 //Roo.log(pane.title);
25563 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25564 // technically we should have a deactivate event.. but maybe add later.
25565 // and it should not de-activate the selected tab...
25566 this.fireEvent('activatepane', pane);
25567 pane.el.addClass('active');
25568 pane.fireEvent('activate');
25573 getActivePane : function()
25576 Roo.each(this.panes, function(p) {
25577 if(p.el.hasClass('active')){
25598 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25600 * @class Roo.bootstrap.TabPane
25601 * @extends Roo.bootstrap.Component
25602 * Bootstrap TabPane class
25603 * @cfg {Boolean} active (false | true) Default false
25604 * @cfg {String} title title of panel
25608 * Create a new TabPane
25609 * @param {Object} config The config object
25612 Roo.bootstrap.dash.TabPane = function(config){
25613 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25619 * When a pane is activated
25620 * @param {Roo.bootstrap.dash.TabPane} pane
25627 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25632 // the tabBox that this is attached to.
25635 getAutoCreate : function()
25643 cfg.cls += ' active';
25648 initEvents : function()
25650 //Roo.log('trigger add pane handler');
25651 this.parent().fireEvent('addpane', this)
25655 * Updates the tab title
25656 * @param {String} html to set the title to.
25658 setTitle: function(str)
25664 this.tab.select('a', true).first().dom.innerHTML = str;
25681 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25684 * @class Roo.bootstrap.menu.Menu
25685 * @extends Roo.bootstrap.Component
25686 * Bootstrap Menu class - container for Menu
25687 * @cfg {String} html Text of the menu
25688 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25689 * @cfg {String} icon Font awesome icon
25690 * @cfg {String} pos Menu align to (top | bottom) default bottom
25694 * Create a new Menu
25695 * @param {Object} config The config object
25699 Roo.bootstrap.menu.Menu = function(config){
25700 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25704 * @event beforeshow
25705 * Fires before this menu is displayed
25706 * @param {Roo.bootstrap.menu.Menu} this
25710 * @event beforehide
25711 * Fires before this menu is hidden
25712 * @param {Roo.bootstrap.menu.Menu} this
25717 * Fires after this menu is displayed
25718 * @param {Roo.bootstrap.menu.Menu} this
25723 * Fires after this menu is hidden
25724 * @param {Roo.bootstrap.menu.Menu} this
25729 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25730 * @param {Roo.bootstrap.menu.Menu} this
25731 * @param {Roo.EventObject} e
25738 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25742 weight : 'default',
25747 getChildContainer : function() {
25748 if(this.isSubMenu){
25752 return this.el.select('ul.dropdown-menu', true).first();
25755 getAutoCreate : function()
25760 cls : 'roo-menu-text',
25768 cls : 'fa ' + this.icon
25779 cls : 'dropdown-button btn btn-' + this.weight,
25784 cls : 'dropdown-toggle btn btn-' + this.weight,
25794 cls : 'dropdown-menu'
25800 if(this.pos == 'top'){
25801 cfg.cls += ' dropup';
25804 if(this.isSubMenu){
25807 cls : 'dropdown-menu'
25814 onRender : function(ct, position)
25816 this.isSubMenu = ct.hasClass('dropdown-submenu');
25818 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25821 initEvents : function()
25823 if(this.isSubMenu){
25827 this.hidden = true;
25829 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25830 this.triggerEl.on('click', this.onTriggerPress, this);
25832 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25833 this.buttonEl.on('click', this.onClick, this);
25839 if(this.isSubMenu){
25843 return this.el.select('ul.dropdown-menu', true).first();
25846 onClick : function(e)
25848 this.fireEvent("click", this, e);
25851 onTriggerPress : function(e)
25853 if (this.isVisible()) {
25860 isVisible : function(){
25861 return !this.hidden;
25866 this.fireEvent("beforeshow", this);
25868 this.hidden = false;
25869 this.el.addClass('open');
25871 Roo.get(document).on("mouseup", this.onMouseUp, this);
25873 this.fireEvent("show", this);
25880 this.fireEvent("beforehide", this);
25882 this.hidden = true;
25883 this.el.removeClass('open');
25885 Roo.get(document).un("mouseup", this.onMouseUp);
25887 this.fireEvent("hide", this);
25890 onMouseUp : function()
25904 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25907 * @class Roo.bootstrap.menu.Item
25908 * @extends Roo.bootstrap.Component
25909 * Bootstrap MenuItem class
25910 * @cfg {Boolean} submenu (true | false) default false
25911 * @cfg {String} html text of the item
25912 * @cfg {String} href the link
25913 * @cfg {Boolean} disable (true | false) default false
25914 * @cfg {Boolean} preventDefault (true | false) default true
25915 * @cfg {String} icon Font awesome icon
25916 * @cfg {String} pos Submenu align to (left | right) default right
25920 * Create a new Item
25921 * @param {Object} config The config object
25925 Roo.bootstrap.menu.Item = function(config){
25926 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25930 * Fires when the mouse is hovering over this menu
25931 * @param {Roo.bootstrap.menu.Item} this
25932 * @param {Roo.EventObject} e
25937 * Fires when the mouse exits this menu
25938 * @param {Roo.bootstrap.menu.Item} this
25939 * @param {Roo.EventObject} e
25945 * The raw click event for the entire grid.
25946 * @param {Roo.EventObject} e
25952 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25957 preventDefault: true,
25962 getAutoCreate : function()
25967 cls : 'roo-menu-item-text',
25975 cls : 'fa ' + this.icon
25984 href : this.href || '#',
25991 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25995 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25997 if(this.pos == 'left'){
25998 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26005 initEvents : function()
26007 this.el.on('mouseover', this.onMouseOver, this);
26008 this.el.on('mouseout', this.onMouseOut, this);
26010 this.el.select('a', true).first().on('click', this.onClick, this);
26014 onClick : function(e)
26016 if(this.preventDefault){
26017 e.preventDefault();
26020 this.fireEvent("click", this, e);
26023 onMouseOver : function(e)
26025 if(this.submenu && this.pos == 'left'){
26026 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26029 this.fireEvent("mouseover", this, e);
26032 onMouseOut : function(e)
26034 this.fireEvent("mouseout", this, e);
26046 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26049 * @class Roo.bootstrap.menu.Separator
26050 * @extends Roo.bootstrap.Component
26051 * Bootstrap Separator class
26054 * Create a new Separator
26055 * @param {Object} config The config object
26059 Roo.bootstrap.menu.Separator = function(config){
26060 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26063 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26065 getAutoCreate : function(){
26086 * @class Roo.bootstrap.Tooltip
26087 * Bootstrap Tooltip class
26088 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26089 * to determine which dom element triggers the tooltip.
26091 * It needs to add support for additional attributes like tooltip-position
26094 * Create a new Toolti
26095 * @param {Object} config The config object
26098 Roo.bootstrap.Tooltip = function(config){
26099 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26101 this.alignment = Roo.bootstrap.Tooltip.alignment;
26103 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26104 this.alignment = config.alignment;
26109 Roo.apply(Roo.bootstrap.Tooltip, {
26111 * @function init initialize tooltip monitoring.
26115 currentTip : false,
26116 currentRegion : false,
26122 Roo.get(document).on('mouseover', this.enter ,this);
26123 Roo.get(document).on('mouseout', this.leave, this);
26126 this.currentTip = new Roo.bootstrap.Tooltip();
26129 enter : function(ev)
26131 var dom = ev.getTarget();
26133 //Roo.log(['enter',dom]);
26134 var el = Roo.fly(dom);
26135 if (this.currentEl) {
26137 //Roo.log(this.currentEl);
26138 //Roo.log(this.currentEl.contains(dom));
26139 if (this.currentEl == el) {
26142 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26148 if (this.currentTip.el) {
26149 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26153 if(!el || el.dom == document){
26159 // you can not look for children, as if el is the body.. then everythign is the child..
26160 if (!el.attr('tooltip')) { //
26161 if (!el.select("[tooltip]").elements.length) {
26164 // is the mouse over this child...?
26165 bindEl = el.select("[tooltip]").first();
26166 var xy = ev.getXY();
26167 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26168 //Roo.log("not in region.");
26171 //Roo.log("child element over..");
26174 this.currentEl = bindEl;
26175 this.currentTip.bind(bindEl);
26176 this.currentRegion = Roo.lib.Region.getRegion(dom);
26177 this.currentTip.enter();
26180 leave : function(ev)
26182 var dom = ev.getTarget();
26183 //Roo.log(['leave',dom]);
26184 if (!this.currentEl) {
26189 if (dom != this.currentEl.dom) {
26192 var xy = ev.getXY();
26193 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26196 // only activate leave if mouse cursor is outside... bounding box..
26201 if (this.currentTip) {
26202 this.currentTip.leave();
26204 //Roo.log('clear currentEl');
26205 this.currentEl = false;
26210 'left' : ['r-l', [-2,0], 'right'],
26211 'right' : ['l-r', [2,0], 'left'],
26212 'bottom' : ['t-b', [0,2], 'top'],
26213 'top' : [ 'b-t', [0,-2], 'bottom']
26219 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26224 delay : null, // can be { show : 300 , hide: 500}
26228 hoverState : null, //???
26230 placement : 'bottom',
26234 getAutoCreate : function(){
26241 cls : 'tooltip-arrow'
26244 cls : 'tooltip-inner'
26251 bind : function(el)
26257 enter : function () {
26259 if (this.timeout != null) {
26260 clearTimeout(this.timeout);
26263 this.hoverState = 'in';
26264 //Roo.log("enter - show");
26265 if (!this.delay || !this.delay.show) {
26270 this.timeout = setTimeout(function () {
26271 if (_t.hoverState == 'in') {
26274 }, this.delay.show);
26278 clearTimeout(this.timeout);
26280 this.hoverState = 'out';
26281 if (!this.delay || !this.delay.hide) {
26287 this.timeout = setTimeout(function () {
26288 //Roo.log("leave - timeout");
26290 if (_t.hoverState == 'out') {
26292 Roo.bootstrap.Tooltip.currentEl = false;
26297 show : function (msg)
26300 this.render(document.body);
26303 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26305 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26307 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26309 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26311 var placement = typeof this.placement == 'function' ?
26312 this.placement.call(this, this.el, on_el) :
26315 var autoToken = /\s?auto?\s?/i;
26316 var autoPlace = autoToken.test(placement);
26318 placement = placement.replace(autoToken, '') || 'top';
26322 //this.el.setXY([0,0]);
26324 //this.el.dom.style.display='block';
26326 //this.el.appendTo(on_el);
26328 var p = this.getPosition();
26329 var box = this.el.getBox();
26335 var align = this.alignment[placement];
26337 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26339 if(placement == 'top' || placement == 'bottom'){
26341 placement = 'right';
26344 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26345 placement = 'left';
26348 var scroll = Roo.select('body', true).first().getScroll();
26350 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26354 align = this.alignment[placement];
26357 this.el.alignTo(this.bindEl, align[0],align[1]);
26358 //var arrow = this.el.select('.arrow',true).first();
26359 //arrow.set(align[2],
26361 this.el.addClass(placement);
26363 this.el.addClass('in fade');
26365 this.hoverState = null;
26367 if (this.el.hasClass('fade')) {
26378 //this.el.setXY([0,0]);
26379 this.el.removeClass('in');
26395 * @class Roo.bootstrap.LocationPicker
26396 * @extends Roo.bootstrap.Component
26397 * Bootstrap LocationPicker class
26398 * @cfg {Number} latitude Position when init default 0
26399 * @cfg {Number} longitude Position when init default 0
26400 * @cfg {Number} zoom default 15
26401 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26402 * @cfg {Boolean} mapTypeControl default false
26403 * @cfg {Boolean} disableDoubleClickZoom default false
26404 * @cfg {Boolean} scrollwheel default true
26405 * @cfg {Boolean} streetViewControl default false
26406 * @cfg {Number} radius default 0
26407 * @cfg {String} locationName
26408 * @cfg {Boolean} draggable default true
26409 * @cfg {Boolean} enableAutocomplete default false
26410 * @cfg {Boolean} enableReverseGeocode default true
26411 * @cfg {String} markerTitle
26414 * Create a new LocationPicker
26415 * @param {Object} config The config object
26419 Roo.bootstrap.LocationPicker = function(config){
26421 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26426 * Fires when the picker initialized.
26427 * @param {Roo.bootstrap.LocationPicker} this
26428 * @param {Google Location} location
26432 * @event positionchanged
26433 * Fires when the picker position changed.
26434 * @param {Roo.bootstrap.LocationPicker} this
26435 * @param {Google Location} location
26437 positionchanged : true,
26440 * Fires when the map resize.
26441 * @param {Roo.bootstrap.LocationPicker} this
26446 * Fires when the map show.
26447 * @param {Roo.bootstrap.LocationPicker} this
26452 * Fires when the map hide.
26453 * @param {Roo.bootstrap.LocationPicker} this
26458 * Fires when click the map.
26459 * @param {Roo.bootstrap.LocationPicker} this
26460 * @param {Map event} e
26464 * @event mapRightClick
26465 * Fires when right click the map.
26466 * @param {Roo.bootstrap.LocationPicker} this
26467 * @param {Map event} e
26469 mapRightClick : true,
26471 * @event markerClick
26472 * Fires when click the marker.
26473 * @param {Roo.bootstrap.LocationPicker} this
26474 * @param {Map event} e
26476 markerClick : true,
26478 * @event markerRightClick
26479 * Fires when right click the marker.
26480 * @param {Roo.bootstrap.LocationPicker} this
26481 * @param {Map event} e
26483 markerRightClick : true,
26485 * @event OverlayViewDraw
26486 * Fires when OverlayView Draw
26487 * @param {Roo.bootstrap.LocationPicker} this
26489 OverlayViewDraw : true,
26491 * @event OverlayViewOnAdd
26492 * Fires when OverlayView Draw
26493 * @param {Roo.bootstrap.LocationPicker} this
26495 OverlayViewOnAdd : true,
26497 * @event OverlayViewOnRemove
26498 * Fires when OverlayView Draw
26499 * @param {Roo.bootstrap.LocationPicker} this
26501 OverlayViewOnRemove : true,
26503 * @event OverlayViewShow
26504 * Fires when OverlayView Draw
26505 * @param {Roo.bootstrap.LocationPicker} this
26506 * @param {Pixel} cpx
26508 OverlayViewShow : true,
26510 * @event OverlayViewHide
26511 * Fires when OverlayView Draw
26512 * @param {Roo.bootstrap.LocationPicker} this
26514 OverlayViewHide : true,
26516 * @event loadexception
26517 * Fires when load google lib failed.
26518 * @param {Roo.bootstrap.LocationPicker} this
26520 loadexception : true
26525 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26527 gMapContext: false,
26533 mapTypeControl: false,
26534 disableDoubleClickZoom: false,
26536 streetViewControl: false,
26540 enableAutocomplete: false,
26541 enableReverseGeocode: true,
26544 getAutoCreate: function()
26549 cls: 'roo-location-picker'
26555 initEvents: function(ct, position)
26557 if(!this.el.getWidth() || this.isApplied()){
26561 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26566 initial: function()
26568 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26569 this.fireEvent('loadexception', this);
26573 if(!this.mapTypeId){
26574 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26577 this.gMapContext = this.GMapContext();
26579 this.initOverlayView();
26581 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26585 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26586 _this.setPosition(_this.gMapContext.marker.position);
26589 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26590 _this.fireEvent('mapClick', this, event);
26594 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26595 _this.fireEvent('mapRightClick', this, event);
26599 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26600 _this.fireEvent('markerClick', this, event);
26604 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26605 _this.fireEvent('markerRightClick', this, event);
26609 this.setPosition(this.gMapContext.location);
26611 this.fireEvent('initial', this, this.gMapContext.location);
26614 initOverlayView: function()
26618 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26622 _this.fireEvent('OverlayViewDraw', _this);
26627 _this.fireEvent('OverlayViewOnAdd', _this);
26630 onRemove: function()
26632 _this.fireEvent('OverlayViewOnRemove', _this);
26635 show: function(cpx)
26637 _this.fireEvent('OverlayViewShow', _this, cpx);
26642 _this.fireEvent('OverlayViewHide', _this);
26648 fromLatLngToContainerPixel: function(event)
26650 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26653 isApplied: function()
26655 return this.getGmapContext() == false ? false : true;
26658 getGmapContext: function()
26660 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26663 GMapContext: function()
26665 var position = new google.maps.LatLng(this.latitude, this.longitude);
26667 var _map = new google.maps.Map(this.el.dom, {
26670 mapTypeId: this.mapTypeId,
26671 mapTypeControl: this.mapTypeControl,
26672 disableDoubleClickZoom: this.disableDoubleClickZoom,
26673 scrollwheel: this.scrollwheel,
26674 streetViewControl: this.streetViewControl,
26675 locationName: this.locationName,
26676 draggable: this.draggable,
26677 enableAutocomplete: this.enableAutocomplete,
26678 enableReverseGeocode: this.enableReverseGeocode
26681 var _marker = new google.maps.Marker({
26682 position: position,
26684 title: this.markerTitle,
26685 draggable: this.draggable
26692 location: position,
26693 radius: this.radius,
26694 locationName: this.locationName,
26695 addressComponents: {
26696 formatted_address: null,
26697 addressLine1: null,
26698 addressLine2: null,
26700 streetNumber: null,
26704 stateOrProvince: null
26707 domContainer: this.el.dom,
26708 geodecoder: new google.maps.Geocoder()
26712 drawCircle: function(center, radius, options)
26714 if (this.gMapContext.circle != null) {
26715 this.gMapContext.circle.setMap(null);
26719 options = Roo.apply({}, options, {
26720 strokeColor: "#0000FF",
26721 strokeOpacity: .35,
26723 fillColor: "#0000FF",
26727 options.map = this.gMapContext.map;
26728 options.radius = radius;
26729 options.center = center;
26730 this.gMapContext.circle = new google.maps.Circle(options);
26731 return this.gMapContext.circle;
26737 setPosition: function(location)
26739 this.gMapContext.location = location;
26740 this.gMapContext.marker.setPosition(location);
26741 this.gMapContext.map.panTo(location);
26742 this.drawCircle(location, this.gMapContext.radius, {});
26746 if (this.gMapContext.settings.enableReverseGeocode) {
26747 this.gMapContext.geodecoder.geocode({
26748 latLng: this.gMapContext.location
26749 }, function(results, status) {
26751 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26752 _this.gMapContext.locationName = results[0].formatted_address;
26753 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26755 _this.fireEvent('positionchanged', this, location);
26762 this.fireEvent('positionchanged', this, location);
26767 google.maps.event.trigger(this.gMapContext.map, "resize");
26769 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26771 this.fireEvent('resize', this);
26774 setPositionByLatLng: function(latitude, longitude)
26776 this.setPosition(new google.maps.LatLng(latitude, longitude));
26779 getCurrentPosition: function()
26782 latitude: this.gMapContext.location.lat(),
26783 longitude: this.gMapContext.location.lng()
26787 getAddressName: function()
26789 return this.gMapContext.locationName;
26792 getAddressComponents: function()
26794 return this.gMapContext.addressComponents;
26797 address_component_from_google_geocode: function(address_components)
26801 for (var i = 0; i < address_components.length; i++) {
26802 var component = address_components[i];
26803 if (component.types.indexOf("postal_code") >= 0) {
26804 result.postalCode = component.short_name;
26805 } else if (component.types.indexOf("street_number") >= 0) {
26806 result.streetNumber = component.short_name;
26807 } else if (component.types.indexOf("route") >= 0) {
26808 result.streetName = component.short_name;
26809 } else if (component.types.indexOf("neighborhood") >= 0) {
26810 result.city = component.short_name;
26811 } else if (component.types.indexOf("locality") >= 0) {
26812 result.city = component.short_name;
26813 } else if (component.types.indexOf("sublocality") >= 0) {
26814 result.district = component.short_name;
26815 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26816 result.stateOrProvince = component.short_name;
26817 } else if (component.types.indexOf("country") >= 0) {
26818 result.country = component.short_name;
26822 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26823 result.addressLine2 = "";
26827 setZoomLevel: function(zoom)
26829 this.gMapContext.map.setZoom(zoom);
26842 this.fireEvent('show', this);
26853 this.fireEvent('hide', this);
26858 Roo.apply(Roo.bootstrap.LocationPicker, {
26860 OverlayView : function(map, options)
26862 options = options || {};
26876 * @class Roo.bootstrap.Alert
26877 * @extends Roo.bootstrap.Component
26878 * Bootstrap Alert class
26879 * @cfg {String} title The title of alert
26880 * @cfg {String} html The content of alert
26881 * @cfg {String} weight ( success | info | warning | danger )
26882 * @cfg {String} faicon font-awesomeicon
26885 * Create a new alert
26886 * @param {Object} config The config object
26890 Roo.bootstrap.Alert = function(config){
26891 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26895 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26902 getAutoCreate : function()
26911 cls : 'roo-alert-icon'
26916 cls : 'roo-alert-title',
26921 cls : 'roo-alert-text',
26928 cfg.cn[0].cls += ' fa ' + this.faicon;
26932 cfg.cls += ' alert-' + this.weight;
26938 initEvents: function()
26940 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26943 setTitle : function(str)
26945 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26948 setText : function(str)
26950 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26953 setWeight : function(weight)
26956 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26959 this.weight = weight;
26961 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26964 setIcon : function(icon)
26967 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26970 this.faicon = icon;
26972 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26993 * @class Roo.bootstrap.UploadCropbox
26994 * @extends Roo.bootstrap.Component
26995 * Bootstrap UploadCropbox class
26996 * @cfg {String} emptyText show when image has been loaded
26997 * @cfg {String} rotateNotify show when image too small to rotate
26998 * @cfg {Number} errorTimeout default 3000
26999 * @cfg {Number} minWidth default 300
27000 * @cfg {Number} minHeight default 300
27001 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27002 * @cfg {Boolean} isDocument (true|false) default false
27003 * @cfg {String} url action url
27004 * @cfg {String} paramName default 'imageUpload'
27005 * @cfg {String} method default POST
27006 * @cfg {Boolean} loadMask (true|false) default true
27007 * @cfg {Boolean} loadingText default 'Loading...'
27010 * Create a new UploadCropbox
27011 * @param {Object} config The config object
27014 Roo.bootstrap.UploadCropbox = function(config){
27015 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27019 * @event beforeselectfile
27020 * Fire before select file
27021 * @param {Roo.bootstrap.UploadCropbox} this
27023 "beforeselectfile" : true,
27026 * Fire after initEvent
27027 * @param {Roo.bootstrap.UploadCropbox} this
27032 * Fire after initEvent
27033 * @param {Roo.bootstrap.UploadCropbox} this
27034 * @param {String} data
27039 * Fire when preparing the file data
27040 * @param {Roo.bootstrap.UploadCropbox} this
27041 * @param {Object} file
27046 * Fire when get exception
27047 * @param {Roo.bootstrap.UploadCropbox} this
27048 * @param {XMLHttpRequest} xhr
27050 "exception" : true,
27052 * @event beforeloadcanvas
27053 * Fire before load the canvas
27054 * @param {Roo.bootstrap.UploadCropbox} this
27055 * @param {String} src
27057 "beforeloadcanvas" : true,
27060 * Fire when trash image
27061 * @param {Roo.bootstrap.UploadCropbox} this
27066 * Fire when download the image
27067 * @param {Roo.bootstrap.UploadCropbox} this
27071 * @event footerbuttonclick
27072 * Fire when footerbuttonclick
27073 * @param {Roo.bootstrap.UploadCropbox} this
27074 * @param {String} type
27076 "footerbuttonclick" : true,
27080 * @param {Roo.bootstrap.UploadCropbox} this
27085 * Fire when rotate the image
27086 * @param {Roo.bootstrap.UploadCropbox} this
27087 * @param {String} pos
27092 * Fire when inspect the file
27093 * @param {Roo.bootstrap.UploadCropbox} this
27094 * @param {Object} file
27099 * Fire when xhr upload the file
27100 * @param {Roo.bootstrap.UploadCropbox} this
27101 * @param {Object} data
27106 * Fire when arrange the file data
27107 * @param {Roo.bootstrap.UploadCropbox} this
27108 * @param {Object} formData
27113 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27116 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27118 emptyText : 'Click to upload image',
27119 rotateNotify : 'Image is too small to rotate',
27120 errorTimeout : 3000,
27134 cropType : 'image/jpeg',
27136 canvasLoaded : false,
27137 isDocument : false,
27139 paramName : 'imageUpload',
27141 loadingText : 'Loading...',
27144 getAutoCreate : function()
27148 cls : 'roo-upload-cropbox',
27152 cls : 'roo-upload-cropbox-selector',
27157 cls : 'roo-upload-cropbox-body',
27158 style : 'cursor:pointer',
27162 cls : 'roo-upload-cropbox-preview'
27166 cls : 'roo-upload-cropbox-thumb'
27170 cls : 'roo-upload-cropbox-empty-notify',
27171 html : this.emptyText
27175 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27176 html : this.rotateNotify
27182 cls : 'roo-upload-cropbox-footer',
27185 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27195 onRender : function(ct, position)
27197 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27199 if (this.buttons.length) {
27201 Roo.each(this.buttons, function(bb) {
27203 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27205 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27211 this.maskEl = this.el;
27215 initEvents : function()
27217 this.urlAPI = (window.createObjectURL && window) ||
27218 (window.URL && URL.revokeObjectURL && URL) ||
27219 (window.webkitURL && webkitURL);
27221 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27222 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27224 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27225 this.selectorEl.hide();
27227 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27228 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27230 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27231 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27232 this.thumbEl.hide();
27234 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27235 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27237 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27238 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27239 this.errorEl.hide();
27241 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27242 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27243 this.footerEl.hide();
27245 this.setThumbBoxSize();
27251 this.fireEvent('initial', this);
27258 window.addEventListener("resize", function() { _this.resize(); } );
27260 this.bodyEl.on('click', this.beforeSelectFile, this);
27263 this.bodyEl.on('touchstart', this.onTouchStart, this);
27264 this.bodyEl.on('touchmove', this.onTouchMove, this);
27265 this.bodyEl.on('touchend', this.onTouchEnd, this);
27269 this.bodyEl.on('mousedown', this.onMouseDown, this);
27270 this.bodyEl.on('mousemove', this.onMouseMove, this);
27271 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27272 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27273 Roo.get(document).on('mouseup', this.onMouseUp, this);
27276 this.selectorEl.on('change', this.onFileSelected, this);
27282 this.baseScale = 1;
27284 this.baseRotate = 1;
27285 this.dragable = false;
27286 this.pinching = false;
27289 this.cropData = false;
27290 this.notifyEl.dom.innerHTML = this.emptyText;
27292 this.selectorEl.dom.value = '';
27296 resize : function()
27298 if(this.fireEvent('resize', this) != false){
27299 this.setThumbBoxPosition();
27300 this.setCanvasPosition();
27304 onFooterButtonClick : function(e, el, o, type)
27307 case 'rotate-left' :
27308 this.onRotateLeft(e);
27310 case 'rotate-right' :
27311 this.onRotateRight(e);
27314 this.beforeSelectFile(e);
27329 this.fireEvent('footerbuttonclick', this, type);
27332 beforeSelectFile : function(e)
27334 e.preventDefault();
27336 if(this.fireEvent('beforeselectfile', this) != false){
27337 this.selectorEl.dom.click();
27341 onFileSelected : function(e)
27343 e.preventDefault();
27345 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27349 var file = this.selectorEl.dom.files[0];
27351 if(this.fireEvent('inspect', this, file) != false){
27352 this.prepare(file);
27357 trash : function(e)
27359 this.fireEvent('trash', this);
27362 download : function(e)
27364 this.fireEvent('download', this);
27367 loadCanvas : function(src)
27369 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27373 this.imageEl = document.createElement('img');
27377 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27379 this.imageEl.src = src;
27383 onLoadCanvas : function()
27385 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27386 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27388 this.bodyEl.un('click', this.beforeSelectFile, this);
27390 this.notifyEl.hide();
27391 this.thumbEl.show();
27392 this.footerEl.show();
27394 this.baseRotateLevel();
27396 if(this.isDocument){
27397 this.setThumbBoxSize();
27400 this.setThumbBoxPosition();
27402 this.baseScaleLevel();
27408 this.canvasLoaded = true;
27411 this.maskEl.unmask();
27416 setCanvasPosition : function()
27418 if(!this.canvasEl){
27422 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27423 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27425 this.previewEl.setLeft(pw);
27426 this.previewEl.setTop(ph);
27430 onMouseDown : function(e)
27434 this.dragable = true;
27435 this.pinching = false;
27437 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27438 this.dragable = false;
27442 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27443 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27447 onMouseMove : function(e)
27451 if(!this.canvasLoaded){
27455 if (!this.dragable){
27459 var minX = Math.ceil(this.thumbEl.getLeft(true));
27460 var minY = Math.ceil(this.thumbEl.getTop(true));
27462 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27463 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27465 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27466 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27468 x = x - this.mouseX;
27469 y = y - this.mouseY;
27471 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27472 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27474 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27475 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27477 this.previewEl.setLeft(bgX);
27478 this.previewEl.setTop(bgY);
27480 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27481 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27484 onMouseUp : function(e)
27488 this.dragable = false;
27491 onMouseWheel : function(e)
27495 this.startScale = this.scale;
27497 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27499 if(!this.zoomable()){
27500 this.scale = this.startScale;
27509 zoomable : function()
27511 var minScale = this.thumbEl.getWidth() / this.minWidth;
27513 if(this.minWidth < this.minHeight){
27514 minScale = this.thumbEl.getHeight() / this.minHeight;
27517 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27518 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27522 (this.rotate == 0 || this.rotate == 180) &&
27524 width > this.imageEl.OriginWidth ||
27525 height > this.imageEl.OriginHeight ||
27526 (width < this.minWidth && height < this.minHeight)
27534 (this.rotate == 90 || this.rotate == 270) &&
27536 width > this.imageEl.OriginWidth ||
27537 height > this.imageEl.OriginHeight ||
27538 (width < this.minHeight && height < this.minWidth)
27545 !this.isDocument &&
27546 (this.rotate == 0 || this.rotate == 180) &&
27548 width < this.minWidth ||
27549 width > this.imageEl.OriginWidth ||
27550 height < this.minHeight ||
27551 height > this.imageEl.OriginHeight
27558 !this.isDocument &&
27559 (this.rotate == 90 || this.rotate == 270) &&
27561 width < this.minHeight ||
27562 width > this.imageEl.OriginWidth ||
27563 height < this.minWidth ||
27564 height > this.imageEl.OriginHeight
27574 onRotateLeft : function(e)
27576 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27578 var minScale = this.thumbEl.getWidth() / this.minWidth;
27580 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27581 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27583 this.startScale = this.scale;
27585 while (this.getScaleLevel() < minScale){
27587 this.scale = this.scale + 1;
27589 if(!this.zoomable()){
27594 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27595 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27600 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27607 this.scale = this.startScale;
27609 this.onRotateFail();
27614 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27616 if(this.isDocument){
27617 this.setThumbBoxSize();
27618 this.setThumbBoxPosition();
27619 this.setCanvasPosition();
27624 this.fireEvent('rotate', this, 'left');
27628 onRotateRight : function(e)
27630 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27632 var minScale = this.thumbEl.getWidth() / this.minWidth;
27634 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27635 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27637 this.startScale = this.scale;
27639 while (this.getScaleLevel() < minScale){
27641 this.scale = this.scale + 1;
27643 if(!this.zoomable()){
27648 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27649 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27654 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27661 this.scale = this.startScale;
27663 this.onRotateFail();
27668 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27670 if(this.isDocument){
27671 this.setThumbBoxSize();
27672 this.setThumbBoxPosition();
27673 this.setCanvasPosition();
27678 this.fireEvent('rotate', this, 'right');
27681 onRotateFail : function()
27683 this.errorEl.show(true);
27687 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27692 this.previewEl.dom.innerHTML = '';
27694 var canvasEl = document.createElement("canvas");
27696 var contextEl = canvasEl.getContext("2d");
27698 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27699 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27700 var center = this.imageEl.OriginWidth / 2;
27702 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27703 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27704 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27705 center = this.imageEl.OriginHeight / 2;
27708 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27710 contextEl.translate(center, center);
27711 contextEl.rotate(this.rotate * Math.PI / 180);
27713 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27715 this.canvasEl = document.createElement("canvas");
27717 this.contextEl = this.canvasEl.getContext("2d");
27719 switch (this.rotate) {
27722 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27723 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27725 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27730 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27731 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27733 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27734 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);
27738 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27743 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27744 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27746 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27747 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);
27751 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27756 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27757 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27759 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27760 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27764 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);
27771 this.previewEl.appendChild(this.canvasEl);
27773 this.setCanvasPosition();
27778 if(!this.canvasLoaded){
27782 var imageCanvas = document.createElement("canvas");
27784 var imageContext = imageCanvas.getContext("2d");
27786 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27787 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27789 var center = imageCanvas.width / 2;
27791 imageContext.translate(center, center);
27793 imageContext.rotate(this.rotate * Math.PI / 180);
27795 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27797 var canvas = document.createElement("canvas");
27799 var context = canvas.getContext("2d");
27801 canvas.width = this.minWidth;
27802 canvas.height = this.minHeight;
27804 switch (this.rotate) {
27807 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27808 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27810 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27811 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27813 var targetWidth = this.minWidth - 2 * x;
27814 var targetHeight = this.minHeight - 2 * y;
27818 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27819 scale = targetWidth / width;
27822 if(x > 0 && y == 0){
27823 scale = targetHeight / height;
27826 if(x > 0 && y > 0){
27827 scale = targetWidth / width;
27829 if(width < height){
27830 scale = targetHeight / height;
27834 context.scale(scale, scale);
27836 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27837 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27839 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27840 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27842 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27847 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27848 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27850 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27851 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27853 var targetWidth = this.minWidth - 2 * x;
27854 var targetHeight = this.minHeight - 2 * y;
27858 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27859 scale = targetWidth / width;
27862 if(x > 0 && y == 0){
27863 scale = targetHeight / height;
27866 if(x > 0 && y > 0){
27867 scale = targetWidth / width;
27869 if(width < height){
27870 scale = targetHeight / height;
27874 context.scale(scale, scale);
27876 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27877 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27879 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27880 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27882 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27884 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27889 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27890 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27892 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27893 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27895 var targetWidth = this.minWidth - 2 * x;
27896 var targetHeight = this.minHeight - 2 * y;
27900 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27901 scale = targetWidth / width;
27904 if(x > 0 && y == 0){
27905 scale = targetHeight / height;
27908 if(x > 0 && y > 0){
27909 scale = targetWidth / width;
27911 if(width < height){
27912 scale = targetHeight / height;
27916 context.scale(scale, scale);
27918 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27919 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27921 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27922 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27924 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27925 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27927 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27932 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27933 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27935 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27936 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27938 var targetWidth = this.minWidth - 2 * x;
27939 var targetHeight = this.minHeight - 2 * y;
27943 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27944 scale = targetWidth / width;
27947 if(x > 0 && y == 0){
27948 scale = targetHeight / height;
27951 if(x > 0 && y > 0){
27952 scale = targetWidth / width;
27954 if(width < height){
27955 scale = targetHeight / height;
27959 context.scale(scale, scale);
27961 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27962 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27964 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27965 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27967 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27969 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27976 this.cropData = canvas.toDataURL(this.cropType);
27978 if(this.fireEvent('crop', this, this.cropData) !== false){
27979 this.process(this.file, this.cropData);
27986 setThumbBoxSize : function()
27990 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27991 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27992 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27994 this.minWidth = width;
27995 this.minHeight = height;
27997 if(this.rotate == 90 || this.rotate == 270){
27998 this.minWidth = height;
27999 this.minHeight = width;
28004 width = Math.ceil(this.minWidth * height / this.minHeight);
28006 if(this.minWidth > this.minHeight){
28008 height = Math.ceil(this.minHeight * width / this.minWidth);
28011 this.thumbEl.setStyle({
28012 width : width + 'px',
28013 height : height + 'px'
28020 setThumbBoxPosition : function()
28022 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28023 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28025 this.thumbEl.setLeft(x);
28026 this.thumbEl.setTop(y);
28030 baseRotateLevel : function()
28032 this.baseRotate = 1;
28035 typeof(this.exif) != 'undefined' &&
28036 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28037 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28039 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28042 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28046 baseScaleLevel : function()
28050 if(this.isDocument){
28052 if(this.baseRotate == 6 || this.baseRotate == 8){
28054 height = this.thumbEl.getHeight();
28055 this.baseScale = height / this.imageEl.OriginWidth;
28057 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28058 width = this.thumbEl.getWidth();
28059 this.baseScale = width / this.imageEl.OriginHeight;
28065 height = this.thumbEl.getHeight();
28066 this.baseScale = height / this.imageEl.OriginHeight;
28068 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28069 width = this.thumbEl.getWidth();
28070 this.baseScale = width / this.imageEl.OriginWidth;
28076 if(this.baseRotate == 6 || this.baseRotate == 8){
28078 width = this.thumbEl.getHeight();
28079 this.baseScale = width / this.imageEl.OriginHeight;
28081 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28082 height = this.thumbEl.getWidth();
28083 this.baseScale = height / this.imageEl.OriginHeight;
28086 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28087 height = this.thumbEl.getWidth();
28088 this.baseScale = height / this.imageEl.OriginHeight;
28090 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28091 width = this.thumbEl.getHeight();
28092 this.baseScale = width / this.imageEl.OriginWidth;
28099 width = this.thumbEl.getWidth();
28100 this.baseScale = width / this.imageEl.OriginWidth;
28102 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28103 height = this.thumbEl.getHeight();
28104 this.baseScale = height / this.imageEl.OriginHeight;
28107 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28109 height = this.thumbEl.getHeight();
28110 this.baseScale = height / this.imageEl.OriginHeight;
28112 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28113 width = this.thumbEl.getWidth();
28114 this.baseScale = width / this.imageEl.OriginWidth;
28122 getScaleLevel : function()
28124 return this.baseScale * Math.pow(1.1, this.scale);
28127 onTouchStart : function(e)
28129 if(!this.canvasLoaded){
28130 this.beforeSelectFile(e);
28134 var touches = e.browserEvent.touches;
28140 if(touches.length == 1){
28141 this.onMouseDown(e);
28145 if(touches.length != 2){
28151 for(var i = 0, finger; finger = touches[i]; i++){
28152 coords.push(finger.pageX, finger.pageY);
28155 var x = Math.pow(coords[0] - coords[2], 2);
28156 var y = Math.pow(coords[1] - coords[3], 2);
28158 this.startDistance = Math.sqrt(x + y);
28160 this.startScale = this.scale;
28162 this.pinching = true;
28163 this.dragable = false;
28167 onTouchMove : function(e)
28169 if(!this.pinching && !this.dragable){
28173 var touches = e.browserEvent.touches;
28180 this.onMouseMove(e);
28186 for(var i = 0, finger; finger = touches[i]; i++){
28187 coords.push(finger.pageX, finger.pageY);
28190 var x = Math.pow(coords[0] - coords[2], 2);
28191 var y = Math.pow(coords[1] - coords[3], 2);
28193 this.endDistance = Math.sqrt(x + y);
28195 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28197 if(!this.zoomable()){
28198 this.scale = this.startScale;
28206 onTouchEnd : function(e)
28208 this.pinching = false;
28209 this.dragable = false;
28213 process : function(file, crop)
28216 this.maskEl.mask(this.loadingText);
28219 this.xhr = new XMLHttpRequest();
28221 file.xhr = this.xhr;
28223 this.xhr.open(this.method, this.url, true);
28226 "Accept": "application/json",
28227 "Cache-Control": "no-cache",
28228 "X-Requested-With": "XMLHttpRequest"
28231 for (var headerName in headers) {
28232 var headerValue = headers[headerName];
28234 this.xhr.setRequestHeader(headerName, headerValue);
28240 this.xhr.onload = function()
28242 _this.xhrOnLoad(_this.xhr);
28245 this.xhr.onerror = function()
28247 _this.xhrOnError(_this.xhr);
28250 var formData = new FormData();
28252 formData.append('returnHTML', 'NO');
28255 formData.append('crop', crop);
28258 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28259 formData.append(this.paramName, file, file.name);
28262 if(typeof(file.filename) != 'undefined'){
28263 formData.append('filename', file.filename);
28266 if(typeof(file.mimetype) != 'undefined'){
28267 formData.append('mimetype', file.mimetype);
28270 if(this.fireEvent('arrange', this, formData) != false){
28271 this.xhr.send(formData);
28275 xhrOnLoad : function(xhr)
28278 this.maskEl.unmask();
28281 if (xhr.readyState !== 4) {
28282 this.fireEvent('exception', this, xhr);
28286 var response = Roo.decode(xhr.responseText);
28288 if(!response.success){
28289 this.fireEvent('exception', this, xhr);
28293 var response = Roo.decode(xhr.responseText);
28295 this.fireEvent('upload', this, response);
28299 xhrOnError : function()
28302 this.maskEl.unmask();
28305 Roo.log('xhr on error');
28307 var response = Roo.decode(xhr.responseText);
28313 prepare : function(file)
28316 this.maskEl.mask(this.loadingText);
28322 if(typeof(file) === 'string'){
28323 this.loadCanvas(file);
28327 if(!file || !this.urlAPI){
28332 this.cropType = file.type;
28336 if(this.fireEvent('prepare', this, this.file) != false){
28338 var reader = new FileReader();
28340 reader.onload = function (e) {
28341 if (e.target.error) {
28342 Roo.log(e.target.error);
28346 var buffer = e.target.result,
28347 dataView = new DataView(buffer),
28349 maxOffset = dataView.byteLength - 4,
28353 if (dataView.getUint16(0) === 0xffd8) {
28354 while (offset < maxOffset) {
28355 markerBytes = dataView.getUint16(offset);
28357 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28358 markerLength = dataView.getUint16(offset + 2) + 2;
28359 if (offset + markerLength > dataView.byteLength) {
28360 Roo.log('Invalid meta data: Invalid segment size.');
28364 if(markerBytes == 0xffe1){
28365 _this.parseExifData(
28372 offset += markerLength;
28382 var url = _this.urlAPI.createObjectURL(_this.file);
28384 _this.loadCanvas(url);
28389 reader.readAsArrayBuffer(this.file);
28395 parseExifData : function(dataView, offset, length)
28397 var tiffOffset = offset + 10,
28401 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28402 // No Exif data, might be XMP data instead
28406 // Check for the ASCII code for "Exif" (0x45786966):
28407 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28408 // No Exif data, might be XMP data instead
28411 if (tiffOffset + 8 > dataView.byteLength) {
28412 Roo.log('Invalid Exif data: Invalid segment size.');
28415 // Check for the two null bytes:
28416 if (dataView.getUint16(offset + 8) !== 0x0000) {
28417 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28420 // Check the byte alignment:
28421 switch (dataView.getUint16(tiffOffset)) {
28423 littleEndian = true;
28426 littleEndian = false;
28429 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28432 // Check for the TIFF tag marker (0x002A):
28433 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28434 Roo.log('Invalid Exif data: Missing TIFF marker.');
28437 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28438 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28440 this.parseExifTags(
28443 tiffOffset + dirOffset,
28448 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28453 if (dirOffset + 6 > dataView.byteLength) {
28454 Roo.log('Invalid Exif data: Invalid directory offset.');
28457 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28458 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28459 if (dirEndOffset + 4 > dataView.byteLength) {
28460 Roo.log('Invalid Exif data: Invalid directory size.');
28463 for (i = 0; i < tagsNumber; i += 1) {
28467 dirOffset + 2 + 12 * i, // tag offset
28471 // Return the offset to the next directory:
28472 return dataView.getUint32(dirEndOffset, littleEndian);
28475 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28477 var tag = dataView.getUint16(offset, littleEndian);
28479 this.exif[tag] = this.getExifValue(
28483 dataView.getUint16(offset + 2, littleEndian), // tag type
28484 dataView.getUint32(offset + 4, littleEndian), // tag length
28489 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28491 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28500 Roo.log('Invalid Exif data: Invalid tag type.');
28504 tagSize = tagType.size * length;
28505 // Determine if the value is contained in the dataOffset bytes,
28506 // or if the value at the dataOffset is a pointer to the actual data:
28507 dataOffset = tagSize > 4 ?
28508 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28509 if (dataOffset + tagSize > dataView.byteLength) {
28510 Roo.log('Invalid Exif data: Invalid data offset.');
28513 if (length === 1) {
28514 return tagType.getValue(dataView, dataOffset, littleEndian);
28517 for (i = 0; i < length; i += 1) {
28518 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28521 if (tagType.ascii) {
28523 // Concatenate the chars:
28524 for (i = 0; i < values.length; i += 1) {
28526 // Ignore the terminating NULL byte(s):
28527 if (c === '\u0000') {
28539 Roo.apply(Roo.bootstrap.UploadCropbox, {
28541 'Orientation': 0x0112
28545 1: 0, //'top-left',
28547 3: 180, //'bottom-right',
28548 // 4: 'bottom-left',
28550 6: 90, //'right-top',
28551 // 7: 'right-bottom',
28552 8: 270 //'left-bottom'
28556 // byte, 8-bit unsigned int:
28558 getValue: function (dataView, dataOffset) {
28559 return dataView.getUint8(dataOffset);
28563 // ascii, 8-bit byte:
28565 getValue: function (dataView, dataOffset) {
28566 return String.fromCharCode(dataView.getUint8(dataOffset));
28571 // short, 16 bit int:
28573 getValue: function (dataView, dataOffset, littleEndian) {
28574 return dataView.getUint16(dataOffset, littleEndian);
28578 // long, 32 bit int:
28580 getValue: function (dataView, dataOffset, littleEndian) {
28581 return dataView.getUint32(dataOffset, littleEndian);
28585 // rational = two long values, first is numerator, second is denominator:
28587 getValue: function (dataView, dataOffset, littleEndian) {
28588 return dataView.getUint32(dataOffset, littleEndian) /
28589 dataView.getUint32(dataOffset + 4, littleEndian);
28593 // slong, 32 bit signed int:
28595 getValue: function (dataView, dataOffset, littleEndian) {
28596 return dataView.getInt32(dataOffset, littleEndian);
28600 // srational, two slongs, first is numerator, second is denominator:
28602 getValue: function (dataView, dataOffset, littleEndian) {
28603 return dataView.getInt32(dataOffset, littleEndian) /
28604 dataView.getInt32(dataOffset + 4, littleEndian);
28614 cls : 'btn-group roo-upload-cropbox-rotate-left',
28615 action : 'rotate-left',
28619 cls : 'btn btn-default',
28620 html : '<i class="fa fa-undo"></i>'
28626 cls : 'btn-group roo-upload-cropbox-picture',
28627 action : 'picture',
28631 cls : 'btn btn-default',
28632 html : '<i class="fa fa-picture-o"></i>'
28638 cls : 'btn-group roo-upload-cropbox-rotate-right',
28639 action : 'rotate-right',
28643 cls : 'btn btn-default',
28644 html : '<i class="fa fa-repeat"></i>'
28652 cls : 'btn-group roo-upload-cropbox-rotate-left',
28653 action : 'rotate-left',
28657 cls : 'btn btn-default',
28658 html : '<i class="fa fa-undo"></i>'
28664 cls : 'btn-group roo-upload-cropbox-download',
28665 action : 'download',
28669 cls : 'btn btn-default',
28670 html : '<i class="fa fa-download"></i>'
28676 cls : 'btn-group roo-upload-cropbox-crop',
28681 cls : 'btn btn-default',
28682 html : '<i class="fa fa-crop"></i>'
28688 cls : 'btn-group roo-upload-cropbox-trash',
28693 cls : 'btn btn-default',
28694 html : '<i class="fa fa-trash"></i>'
28700 cls : 'btn-group roo-upload-cropbox-rotate-right',
28701 action : 'rotate-right',
28705 cls : 'btn btn-default',
28706 html : '<i class="fa fa-repeat"></i>'
28714 cls : 'btn-group roo-upload-cropbox-rotate-left',
28715 action : 'rotate-left',
28719 cls : 'btn btn-default',
28720 html : '<i class="fa fa-undo"></i>'
28726 cls : 'btn-group roo-upload-cropbox-rotate-right',
28727 action : 'rotate-right',
28731 cls : 'btn btn-default',
28732 html : '<i class="fa fa-repeat"></i>'
28745 * @class Roo.bootstrap.DocumentManager
28746 * @extends Roo.bootstrap.Component
28747 * Bootstrap DocumentManager class
28748 * @cfg {String} paramName default 'imageUpload'
28749 * @cfg {String} toolTipName default 'filename'
28750 * @cfg {String} method default POST
28751 * @cfg {String} url action url
28752 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28753 * @cfg {Boolean} multiple multiple upload default true
28754 * @cfg {Number} thumbSize default 300
28755 * @cfg {String} fieldLabel
28756 * @cfg {Number} labelWidth default 4
28757 * @cfg {String} labelAlign (left|top) default left
28758 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28759 * @cfg {Number} labellg set the width of label (1-12)
28760 * @cfg {Number} labelmd set the width of label (1-12)
28761 * @cfg {Number} labelsm set the width of label (1-12)
28762 * @cfg {Number} labelxs set the width of label (1-12)
28765 * Create a new DocumentManager
28766 * @param {Object} config The config object
28769 Roo.bootstrap.DocumentManager = function(config){
28770 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28773 this.delegates = [];
28778 * Fire when initial the DocumentManager
28779 * @param {Roo.bootstrap.DocumentManager} this
28784 * inspect selected file
28785 * @param {Roo.bootstrap.DocumentManager} this
28786 * @param {File} file
28791 * Fire when xhr load exception
28792 * @param {Roo.bootstrap.DocumentManager} this
28793 * @param {XMLHttpRequest} xhr
28795 "exception" : true,
28797 * @event afterupload
28798 * Fire when xhr load exception
28799 * @param {Roo.bootstrap.DocumentManager} this
28800 * @param {XMLHttpRequest} xhr
28802 "afterupload" : true,
28805 * prepare the form data
28806 * @param {Roo.bootstrap.DocumentManager} this
28807 * @param {Object} formData
28812 * Fire when remove the file
28813 * @param {Roo.bootstrap.DocumentManager} this
28814 * @param {Object} file
28819 * Fire after refresh the file
28820 * @param {Roo.bootstrap.DocumentManager} this
28825 * Fire after click the image
28826 * @param {Roo.bootstrap.DocumentManager} this
28827 * @param {Object} file
28832 * Fire when upload a image and editable set to true
28833 * @param {Roo.bootstrap.DocumentManager} this
28834 * @param {Object} file
28838 * @event beforeselectfile
28839 * Fire before select file
28840 * @param {Roo.bootstrap.DocumentManager} this
28842 "beforeselectfile" : true,
28845 * Fire before process file
28846 * @param {Roo.bootstrap.DocumentManager} this
28847 * @param {Object} file
28851 * @event previewrendered
28852 * Fire when preview rendered
28853 * @param {Roo.bootstrap.DocumentManager} this
28854 * @param {Object} file
28856 "previewrendered" : true,
28859 "previewResize" : true
28864 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28873 paramName : 'imageUpload',
28874 toolTipName : 'filename',
28877 labelAlign : 'left',
28887 getAutoCreate : function()
28889 var managerWidget = {
28891 cls : 'roo-document-manager',
28895 cls : 'roo-document-manager-selector',
28900 cls : 'roo-document-manager-uploader',
28904 cls : 'roo-document-manager-upload-btn',
28905 html : '<i class="fa fa-plus"></i>'
28916 cls : 'column col-md-12',
28921 if(this.fieldLabel.length){
28926 cls : 'column col-md-12',
28927 html : this.fieldLabel
28931 cls : 'column col-md-12',
28936 if(this.labelAlign == 'left'){
28941 html : this.fieldLabel
28950 if(this.labelWidth > 12){
28951 content[0].style = "width: " + this.labelWidth + 'px';
28954 if(this.labelWidth < 13 && this.labelmd == 0){
28955 this.labelmd = this.labelWidth;
28958 if(this.labellg > 0){
28959 content[0].cls += ' col-lg-' + this.labellg;
28960 content[1].cls += ' col-lg-' + (12 - this.labellg);
28963 if(this.labelmd > 0){
28964 content[0].cls += ' col-md-' + this.labelmd;
28965 content[1].cls += ' col-md-' + (12 - this.labelmd);
28968 if(this.labelsm > 0){
28969 content[0].cls += ' col-sm-' + this.labelsm;
28970 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28973 if(this.labelxs > 0){
28974 content[0].cls += ' col-xs-' + this.labelxs;
28975 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28983 cls : 'row clearfix',
28991 initEvents : function()
28993 this.managerEl = this.el.select('.roo-document-manager', true).first();
28994 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28996 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28997 this.selectorEl.hide();
29000 this.selectorEl.attr('multiple', 'multiple');
29003 this.selectorEl.on('change', this.onFileSelected, this);
29005 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29006 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29008 this.uploader.on('click', this.onUploaderClick, this);
29010 this.renderProgressDialog();
29014 window.addEventListener("resize", function() { _this.refresh(); } );
29016 this.fireEvent('initial', this);
29019 renderProgressDialog : function()
29023 this.progressDialog = new Roo.bootstrap.Modal({
29024 cls : 'roo-document-manager-progress-dialog',
29025 allow_close : false,
29035 btnclick : function() {
29036 _this.uploadCancel();
29042 this.progressDialog.render(Roo.get(document.body));
29044 this.progress = new Roo.bootstrap.Progress({
29045 cls : 'roo-document-manager-progress',
29050 this.progress.render(this.progressDialog.getChildContainer());
29052 this.progressBar = new Roo.bootstrap.ProgressBar({
29053 cls : 'roo-document-manager-progress-bar',
29056 aria_valuemax : 12,
29060 this.progressBar.render(this.progress.getChildContainer());
29063 onUploaderClick : function(e)
29065 e.preventDefault();
29067 if(this.fireEvent('beforeselectfile', this) != false){
29068 this.selectorEl.dom.click();
29073 onFileSelected : function(e)
29075 e.preventDefault();
29077 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29081 Roo.each(this.selectorEl.dom.files, function(file){
29082 if(this.fireEvent('inspect', this, file) != false){
29083 this.files.push(file);
29093 this.selectorEl.dom.value = '';
29095 if(!this.files || !this.files.length){
29099 if(this.boxes > 0 && this.files.length > this.boxes){
29100 this.files = this.files.slice(0, this.boxes);
29103 this.uploader.show();
29105 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29106 this.uploader.hide();
29115 Roo.each(this.files, function(file){
29117 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29118 var f = this.renderPreview(file);
29123 if(file.type.indexOf('image') != -1){
29124 this.delegates.push(
29126 _this.process(file);
29127 }).createDelegate(this)
29135 _this.process(file);
29136 }).createDelegate(this)
29141 this.files = files;
29143 this.delegates = this.delegates.concat(docs);
29145 if(!this.delegates.length){
29150 this.progressBar.aria_valuemax = this.delegates.length;
29157 arrange : function()
29159 if(!this.delegates.length){
29160 this.progressDialog.hide();
29165 var delegate = this.delegates.shift();
29167 this.progressDialog.show();
29169 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29171 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29176 refresh : function()
29178 this.uploader.show();
29180 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29181 this.uploader.hide();
29184 Roo.isTouch ? this.closable(false) : this.closable(true);
29186 this.fireEvent('refresh', this);
29189 onRemove : function(e, el, o)
29191 e.preventDefault();
29193 this.fireEvent('remove', this, o);
29197 remove : function(o)
29201 Roo.each(this.files, function(file){
29202 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29211 this.files = files;
29218 Roo.each(this.files, function(file){
29223 file.target.remove();
29232 onClick : function(e, el, o)
29234 e.preventDefault();
29236 this.fireEvent('click', this, o);
29240 closable : function(closable)
29242 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29244 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29256 xhrOnLoad : function(xhr)
29258 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29262 if (xhr.readyState !== 4) {
29264 this.fireEvent('exception', this, xhr);
29268 var response = Roo.decode(xhr.responseText);
29270 if(!response.success){
29272 this.fireEvent('exception', this, xhr);
29276 var file = this.renderPreview(response.data);
29278 this.files.push(file);
29282 this.fireEvent('afterupload', this, xhr);
29286 xhrOnError : function(xhr)
29288 Roo.log('xhr on error');
29290 var response = Roo.decode(xhr.responseText);
29297 process : function(file)
29299 if(this.fireEvent('process', this, file) !== false){
29300 if(this.editable && file.type.indexOf('image') != -1){
29301 this.fireEvent('edit', this, file);
29305 this.uploadStart(file, false);
29312 uploadStart : function(file, crop)
29314 this.xhr = new XMLHttpRequest();
29316 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29321 file.xhr = this.xhr;
29323 this.managerEl.createChild({
29325 cls : 'roo-document-manager-loading',
29329 tooltip : file.name,
29330 cls : 'roo-document-manager-thumb',
29331 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29337 this.xhr.open(this.method, this.url, true);
29340 "Accept": "application/json",
29341 "Cache-Control": "no-cache",
29342 "X-Requested-With": "XMLHttpRequest"
29345 for (var headerName in headers) {
29346 var headerValue = headers[headerName];
29348 this.xhr.setRequestHeader(headerName, headerValue);
29354 this.xhr.onload = function()
29356 _this.xhrOnLoad(_this.xhr);
29359 this.xhr.onerror = function()
29361 _this.xhrOnError(_this.xhr);
29364 var formData = new FormData();
29366 formData.append('returnHTML', 'NO');
29369 formData.append('crop', crop);
29372 formData.append(this.paramName, file, file.name);
29379 if(this.fireEvent('prepare', this, formData, options) != false){
29381 if(options.manually){
29385 this.xhr.send(formData);
29389 this.uploadCancel();
29392 uploadCancel : function()
29398 this.delegates = [];
29400 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29407 renderPreview : function(file)
29409 if(typeof(file.target) != 'undefined' && file.target){
29413 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29415 var previewEl = this.managerEl.createChild({
29417 cls : 'roo-document-manager-preview',
29421 tooltip : file[this.toolTipName],
29422 cls : 'roo-document-manager-thumb',
29423 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29428 html : '<i class="fa fa-times-circle"></i>'
29433 var close = previewEl.select('button.close', true).first();
29435 close.on('click', this.onRemove, this, file);
29437 file.target = previewEl;
29439 var image = previewEl.select('img', true).first();
29443 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29445 image.on('click', this.onClick, this, file);
29447 this.fireEvent('previewrendered', this, file);
29453 onPreviewLoad : function(file, image)
29455 if(typeof(file.target) == 'undefined' || !file.target){
29459 var width = image.dom.naturalWidth || image.dom.width;
29460 var height = image.dom.naturalHeight || image.dom.height;
29462 if(!this.previewResize) {
29466 if(width > height){
29467 file.target.addClass('wide');
29471 file.target.addClass('tall');
29476 uploadFromSource : function(file, crop)
29478 this.xhr = new XMLHttpRequest();
29480 this.managerEl.createChild({
29482 cls : 'roo-document-manager-loading',
29486 tooltip : file.name,
29487 cls : 'roo-document-manager-thumb',
29488 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29494 this.xhr.open(this.method, this.url, true);
29497 "Accept": "application/json",
29498 "Cache-Control": "no-cache",
29499 "X-Requested-With": "XMLHttpRequest"
29502 for (var headerName in headers) {
29503 var headerValue = headers[headerName];
29505 this.xhr.setRequestHeader(headerName, headerValue);
29511 this.xhr.onload = function()
29513 _this.xhrOnLoad(_this.xhr);
29516 this.xhr.onerror = function()
29518 _this.xhrOnError(_this.xhr);
29521 var formData = new FormData();
29523 formData.append('returnHTML', 'NO');
29525 formData.append('crop', crop);
29527 if(typeof(file.filename) != 'undefined'){
29528 formData.append('filename', file.filename);
29531 if(typeof(file.mimetype) != 'undefined'){
29532 formData.append('mimetype', file.mimetype);
29537 if(this.fireEvent('prepare', this, formData) != false){
29538 this.xhr.send(formData);
29548 * @class Roo.bootstrap.DocumentViewer
29549 * @extends Roo.bootstrap.Component
29550 * Bootstrap DocumentViewer class
29551 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29552 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29555 * Create a new DocumentViewer
29556 * @param {Object} config The config object
29559 Roo.bootstrap.DocumentViewer = function(config){
29560 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29565 * Fire after initEvent
29566 * @param {Roo.bootstrap.DocumentViewer} this
29572 * @param {Roo.bootstrap.DocumentViewer} this
29577 * Fire after download button
29578 * @param {Roo.bootstrap.DocumentViewer} this
29583 * Fire after trash button
29584 * @param {Roo.bootstrap.DocumentViewer} this
29591 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29593 showDownload : true,
29597 getAutoCreate : function()
29601 cls : 'roo-document-viewer',
29605 cls : 'roo-document-viewer-body',
29609 cls : 'roo-document-viewer-thumb',
29613 cls : 'roo-document-viewer-image'
29621 cls : 'roo-document-viewer-footer',
29624 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29628 cls : 'btn-group roo-document-viewer-download',
29632 cls : 'btn btn-default',
29633 html : '<i class="fa fa-download"></i>'
29639 cls : 'btn-group roo-document-viewer-trash',
29643 cls : 'btn btn-default',
29644 html : '<i class="fa fa-trash"></i>'
29657 initEvents : function()
29659 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29660 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29662 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29663 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29665 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29666 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29668 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29669 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29671 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29672 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29674 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29675 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29677 this.bodyEl.on('click', this.onClick, this);
29678 this.downloadBtn.on('click', this.onDownload, this);
29679 this.trashBtn.on('click', this.onTrash, this);
29681 this.downloadBtn.hide();
29682 this.trashBtn.hide();
29684 if(this.showDownload){
29685 this.downloadBtn.show();
29688 if(this.showTrash){
29689 this.trashBtn.show();
29692 if(!this.showDownload && !this.showTrash) {
29693 this.footerEl.hide();
29698 initial : function()
29700 this.fireEvent('initial', this);
29704 onClick : function(e)
29706 e.preventDefault();
29708 this.fireEvent('click', this);
29711 onDownload : function(e)
29713 e.preventDefault();
29715 this.fireEvent('download', this);
29718 onTrash : function(e)
29720 e.preventDefault();
29722 this.fireEvent('trash', this);
29734 * @class Roo.bootstrap.NavProgressBar
29735 * @extends Roo.bootstrap.Component
29736 * Bootstrap NavProgressBar class
29739 * Create a new nav progress bar
29740 * @param {Object} config The config object
29743 Roo.bootstrap.NavProgressBar = function(config){
29744 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29746 this.bullets = this.bullets || [];
29748 // Roo.bootstrap.NavProgressBar.register(this);
29752 * Fires when the active item changes
29753 * @param {Roo.bootstrap.NavProgressBar} this
29754 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29755 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29762 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29767 getAutoCreate : function()
29769 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29773 cls : 'roo-navigation-bar-group',
29777 cls : 'roo-navigation-top-bar'
29781 cls : 'roo-navigation-bullets-bar',
29785 cls : 'roo-navigation-bar'
29792 cls : 'roo-navigation-bottom-bar'
29802 initEvents: function()
29807 onRender : function(ct, position)
29809 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29811 if(this.bullets.length){
29812 Roo.each(this.bullets, function(b){
29821 addItem : function(cfg)
29823 var item = new Roo.bootstrap.NavProgressItem(cfg);
29825 item.parentId = this.id;
29826 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29829 var top = new Roo.bootstrap.Element({
29831 cls : 'roo-navigation-bar-text'
29834 var bottom = new Roo.bootstrap.Element({
29836 cls : 'roo-navigation-bar-text'
29839 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29840 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29842 var topText = new Roo.bootstrap.Element({
29844 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29847 var bottomText = new Roo.bootstrap.Element({
29849 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29852 topText.onRender(top.el, null);
29853 bottomText.onRender(bottom.el, null);
29856 item.bottomEl = bottom;
29859 this.barItems.push(item);
29864 getActive : function()
29866 var active = false;
29868 Roo.each(this.barItems, function(v){
29870 if (!v.isActive()) {
29882 setActiveItem : function(item)
29886 Roo.each(this.barItems, function(v){
29887 if (v.rid == item.rid) {
29891 if (v.isActive()) {
29892 v.setActive(false);
29897 item.setActive(true);
29899 this.fireEvent('changed', this, item, prev);
29902 getBarItem: function(rid)
29906 Roo.each(this.barItems, function(e) {
29907 if (e.rid != rid) {
29918 indexOfItem : function(item)
29922 Roo.each(this.barItems, function(v, i){
29924 if (v.rid != item.rid) {
29935 setActiveNext : function()
29937 var i = this.indexOfItem(this.getActive());
29939 if (i > this.barItems.length) {
29943 this.setActiveItem(this.barItems[i+1]);
29946 setActivePrev : function()
29948 var i = this.indexOfItem(this.getActive());
29954 this.setActiveItem(this.barItems[i-1]);
29957 format : function()
29959 if(!this.barItems.length){
29963 var width = 100 / this.barItems.length;
29965 Roo.each(this.barItems, function(i){
29966 i.el.setStyle('width', width + '%');
29967 i.topEl.el.setStyle('width', width + '%');
29968 i.bottomEl.el.setStyle('width', width + '%');
29977 * Nav Progress Item
29982 * @class Roo.bootstrap.NavProgressItem
29983 * @extends Roo.bootstrap.Component
29984 * Bootstrap NavProgressItem class
29985 * @cfg {String} rid the reference id
29986 * @cfg {Boolean} active (true|false) Is item active default false
29987 * @cfg {Boolean} disabled (true|false) Is item active default false
29988 * @cfg {String} html
29989 * @cfg {String} position (top|bottom) text position default bottom
29990 * @cfg {String} icon show icon instead of number
29993 * Create a new NavProgressItem
29994 * @param {Object} config The config object
29996 Roo.bootstrap.NavProgressItem = function(config){
29997 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30002 * The raw click event for the entire grid.
30003 * @param {Roo.bootstrap.NavProgressItem} this
30004 * @param {Roo.EventObject} e
30011 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30017 position : 'bottom',
30020 getAutoCreate : function()
30022 var iconCls = 'roo-navigation-bar-item-icon';
30024 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30028 cls: 'roo-navigation-bar-item',
30038 cfg.cls += ' active';
30041 cfg.cls += ' disabled';
30047 disable : function()
30049 this.setDisabled(true);
30052 enable : function()
30054 this.setDisabled(false);
30057 initEvents: function()
30059 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30061 this.iconEl.on('click', this.onClick, this);
30064 onClick : function(e)
30066 e.preventDefault();
30072 if(this.fireEvent('click', this, e) === false){
30076 this.parent().setActiveItem(this);
30079 isActive: function ()
30081 return this.active;
30084 setActive : function(state)
30086 if(this.active == state){
30090 this.active = state;
30093 this.el.addClass('active');
30097 this.el.removeClass('active');
30102 setDisabled : function(state)
30104 if(this.disabled == state){
30108 this.disabled = state;
30111 this.el.addClass('disabled');
30115 this.el.removeClass('disabled');
30118 tooltipEl : function()
30120 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30133 * @class Roo.bootstrap.FieldLabel
30134 * @extends Roo.bootstrap.Component
30135 * Bootstrap FieldLabel class
30136 * @cfg {String} html contents of the element
30137 * @cfg {String} tag tag of the element default label
30138 * @cfg {String} cls class of the element
30139 * @cfg {String} target label target
30140 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30141 * @cfg {String} invalidClass default "text-warning"
30142 * @cfg {String} validClass default "text-success"
30143 * @cfg {String} iconTooltip default "This field is required"
30144 * @cfg {String} indicatorpos (left|right) default left
30147 * Create a new FieldLabel
30148 * @param {Object} config The config object
30151 Roo.bootstrap.FieldLabel = function(config){
30152 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30157 * Fires after the field has been marked as invalid.
30158 * @param {Roo.form.FieldLabel} this
30159 * @param {String} msg The validation message
30164 * Fires after the field has been validated with no errors.
30165 * @param {Roo.form.FieldLabel} this
30171 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30178 invalidClass : 'has-warning',
30179 validClass : 'has-success',
30180 iconTooltip : 'This field is required',
30181 indicatorpos : 'left',
30183 getAutoCreate : function(){
30186 if (!this.allowBlank) {
30192 cls : 'roo-bootstrap-field-label ' + this.cls,
30197 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30198 tooltip : this.iconTooltip
30207 if(this.indicatorpos == 'right'){
30210 cls : 'roo-bootstrap-field-label ' + this.cls,
30219 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30220 tooltip : this.iconTooltip
30229 initEvents: function()
30231 Roo.bootstrap.Element.superclass.initEvents.call(this);
30233 this.indicator = this.indicatorEl();
30235 if(this.indicator){
30236 this.indicator.removeClass('visible');
30237 this.indicator.addClass('invisible');
30240 Roo.bootstrap.FieldLabel.register(this);
30243 indicatorEl : function()
30245 var indicator = this.el.select('i.roo-required-indicator',true).first();
30256 * Mark this field as valid
30258 markValid : function()
30260 if(this.indicator){
30261 this.indicator.removeClass('visible');
30262 this.indicator.addClass('invisible');
30265 this.el.removeClass(this.invalidClass);
30267 this.el.addClass(this.validClass);
30269 this.fireEvent('valid', this);
30273 * Mark this field as invalid
30274 * @param {String} msg The validation message
30276 markInvalid : function(msg)
30278 if(this.indicator){
30279 this.indicator.removeClass('invisible');
30280 this.indicator.addClass('visible');
30283 this.el.removeClass(this.validClass);
30285 this.el.addClass(this.invalidClass);
30287 this.fireEvent('invalid', this, msg);
30293 Roo.apply(Roo.bootstrap.FieldLabel, {
30298 * register a FieldLabel Group
30299 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30301 register : function(label)
30303 if(this.groups.hasOwnProperty(label.target)){
30307 this.groups[label.target] = label;
30311 * fetch a FieldLabel Group based on the target
30312 * @param {string} target
30313 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30315 get: function(target) {
30316 if (typeof(this.groups[target]) == 'undefined') {
30320 return this.groups[target] ;
30329 * page DateSplitField.
30335 * @class Roo.bootstrap.DateSplitField
30336 * @extends Roo.bootstrap.Component
30337 * Bootstrap DateSplitField class
30338 * @cfg {string} fieldLabel - the label associated
30339 * @cfg {Number} labelWidth set the width of label (0-12)
30340 * @cfg {String} labelAlign (top|left)
30341 * @cfg {Boolean} dayAllowBlank (true|false) default false
30342 * @cfg {Boolean} monthAllowBlank (true|false) default false
30343 * @cfg {Boolean} yearAllowBlank (true|false) default false
30344 * @cfg {string} dayPlaceholder
30345 * @cfg {string} monthPlaceholder
30346 * @cfg {string} yearPlaceholder
30347 * @cfg {string} dayFormat default 'd'
30348 * @cfg {string} monthFormat default 'm'
30349 * @cfg {string} yearFormat default 'Y'
30350 * @cfg {Number} labellg set the width of label (1-12)
30351 * @cfg {Number} labelmd set the width of label (1-12)
30352 * @cfg {Number} labelsm set the width of label (1-12)
30353 * @cfg {Number} labelxs set the width of label (1-12)
30357 * Create a new DateSplitField
30358 * @param {Object} config The config object
30361 Roo.bootstrap.DateSplitField = function(config){
30362 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30368 * getting the data of years
30369 * @param {Roo.bootstrap.DateSplitField} this
30370 * @param {Object} years
30375 * getting the data of days
30376 * @param {Roo.bootstrap.DateSplitField} this
30377 * @param {Object} days
30382 * Fires after the field has been marked as invalid.
30383 * @param {Roo.form.Field} this
30384 * @param {String} msg The validation message
30389 * Fires after the field has been validated with no errors.
30390 * @param {Roo.form.Field} this
30396 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30399 labelAlign : 'top',
30401 dayAllowBlank : false,
30402 monthAllowBlank : false,
30403 yearAllowBlank : false,
30404 dayPlaceholder : '',
30405 monthPlaceholder : '',
30406 yearPlaceholder : '',
30410 isFormField : true,
30416 getAutoCreate : function()
30420 cls : 'row roo-date-split-field-group',
30425 cls : 'form-hidden-field roo-date-split-field-group-value',
30431 var labelCls = 'col-md-12';
30432 var contentCls = 'col-md-4';
30434 if(this.fieldLabel){
30438 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30442 html : this.fieldLabel
30447 if(this.labelAlign == 'left'){
30449 if(this.labelWidth > 12){
30450 label.style = "width: " + this.labelWidth + 'px';
30453 if(this.labelWidth < 13 && this.labelmd == 0){
30454 this.labelmd = this.labelWidth;
30457 if(this.labellg > 0){
30458 labelCls = ' col-lg-' + this.labellg;
30459 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30462 if(this.labelmd > 0){
30463 labelCls = ' col-md-' + this.labelmd;
30464 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30467 if(this.labelsm > 0){
30468 labelCls = ' col-sm-' + this.labelsm;
30469 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30472 if(this.labelxs > 0){
30473 labelCls = ' col-xs-' + this.labelxs;
30474 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30478 label.cls += ' ' + labelCls;
30480 cfg.cn.push(label);
30483 Roo.each(['day', 'month', 'year'], function(t){
30486 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30493 inputEl: function ()
30495 return this.el.select('.roo-date-split-field-group-value', true).first();
30498 onRender : function(ct, position)
30502 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30504 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30506 this.dayField = new Roo.bootstrap.ComboBox({
30507 allowBlank : this.dayAllowBlank,
30508 alwaysQuery : true,
30509 displayField : 'value',
30512 forceSelection : true,
30514 placeholder : this.dayPlaceholder,
30515 selectOnFocus : true,
30516 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30517 triggerAction : 'all',
30519 valueField : 'value',
30520 store : new Roo.data.SimpleStore({
30521 data : (function() {
30523 _this.fireEvent('days', _this, days);
30526 fields : [ 'value' ]
30529 select : function (_self, record, index)
30531 _this.setValue(_this.getValue());
30536 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30538 this.monthField = new Roo.bootstrap.MonthField({
30539 after : '<i class=\"fa fa-calendar\"></i>',
30540 allowBlank : this.monthAllowBlank,
30541 placeholder : this.monthPlaceholder,
30544 render : function (_self)
30546 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30547 e.preventDefault();
30551 select : function (_self, oldvalue, newvalue)
30553 _this.setValue(_this.getValue());
30558 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30560 this.yearField = new Roo.bootstrap.ComboBox({
30561 allowBlank : this.yearAllowBlank,
30562 alwaysQuery : true,
30563 displayField : 'value',
30566 forceSelection : true,
30568 placeholder : this.yearPlaceholder,
30569 selectOnFocus : true,
30570 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30571 triggerAction : 'all',
30573 valueField : 'value',
30574 store : new Roo.data.SimpleStore({
30575 data : (function() {
30577 _this.fireEvent('years', _this, years);
30580 fields : [ 'value' ]
30583 select : function (_self, record, index)
30585 _this.setValue(_this.getValue());
30590 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30593 setValue : function(v, format)
30595 this.inputEl.dom.value = v;
30597 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30599 var d = Date.parseDate(v, f);
30606 this.setDay(d.format(this.dayFormat));
30607 this.setMonth(d.format(this.monthFormat));
30608 this.setYear(d.format(this.yearFormat));
30615 setDay : function(v)
30617 this.dayField.setValue(v);
30618 this.inputEl.dom.value = this.getValue();
30623 setMonth : function(v)
30625 this.monthField.setValue(v, true);
30626 this.inputEl.dom.value = this.getValue();
30631 setYear : function(v)
30633 this.yearField.setValue(v);
30634 this.inputEl.dom.value = this.getValue();
30639 getDay : function()
30641 return this.dayField.getValue();
30644 getMonth : function()
30646 return this.monthField.getValue();
30649 getYear : function()
30651 return this.yearField.getValue();
30654 getValue : function()
30656 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30658 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30668 this.inputEl.dom.value = '';
30673 validate : function()
30675 var d = this.dayField.validate();
30676 var m = this.monthField.validate();
30677 var y = this.yearField.validate();
30682 (!this.dayAllowBlank && !d) ||
30683 (!this.monthAllowBlank && !m) ||
30684 (!this.yearAllowBlank && !y)
30689 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30698 this.markInvalid();
30703 markValid : function()
30706 var label = this.el.select('label', true).first();
30707 var icon = this.el.select('i.fa-star', true).first();
30713 this.fireEvent('valid', this);
30717 * Mark this field as invalid
30718 * @param {String} msg The validation message
30720 markInvalid : function(msg)
30723 var label = this.el.select('label', true).first();
30724 var icon = this.el.select('i.fa-star', true).first();
30726 if(label && !icon){
30727 this.el.select('.roo-date-split-field-label', true).createChild({
30729 cls : 'text-danger fa fa-lg fa-star',
30730 tooltip : 'This field is required',
30731 style : 'margin-right:5px;'
30735 this.fireEvent('invalid', this, msg);
30738 clearInvalid : function()
30740 var label = this.el.select('label', true).first();
30741 var icon = this.el.select('i.fa-star', true).first();
30747 this.fireEvent('valid', this);
30750 getName: function()
30760 * http://masonry.desandro.com
30762 * The idea is to render all the bricks based on vertical width...
30764 * The original code extends 'outlayer' - we might need to use that....
30770 * @class Roo.bootstrap.LayoutMasonry
30771 * @extends Roo.bootstrap.Component
30772 * Bootstrap Layout Masonry class
30775 * Create a new Element
30776 * @param {Object} config The config object
30779 Roo.bootstrap.LayoutMasonry = function(config){
30781 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30785 Roo.bootstrap.LayoutMasonry.register(this);
30791 * Fire after layout the items
30792 * @param {Roo.bootstrap.LayoutMasonry} this
30793 * @param {Roo.EventObject} e
30800 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30803 * @cfg {Boolean} isLayoutInstant = no animation?
30805 isLayoutInstant : false, // needed?
30808 * @cfg {Number} boxWidth width of the columns
30813 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30818 * @cfg {Number} padWidth padding below box..
30823 * @cfg {Number} gutter gutter width..
30828 * @cfg {Number} maxCols maximum number of columns
30834 * @cfg {Boolean} isAutoInitial defalut true
30836 isAutoInitial : true,
30841 * @cfg {Boolean} isHorizontal defalut false
30843 isHorizontal : false,
30845 currentSize : null,
30851 bricks: null, //CompositeElement
30855 _isLayoutInited : false,
30857 // isAlternative : false, // only use for vertical layout...
30860 * @cfg {Number} alternativePadWidth padding below box..
30862 alternativePadWidth : 50,
30864 selectedBrick : [],
30866 getAutoCreate : function(){
30868 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30872 cls: 'blog-masonary-wrapper ' + this.cls,
30874 cls : 'mas-boxes masonary'
30881 getChildContainer: function( )
30883 if (this.boxesEl) {
30884 return this.boxesEl;
30887 this.boxesEl = this.el.select('.mas-boxes').first();
30889 return this.boxesEl;
30893 initEvents : function()
30897 if(this.isAutoInitial){
30898 Roo.log('hook children rendered');
30899 this.on('childrenrendered', function() {
30900 Roo.log('children rendered');
30906 initial : function()
30908 this.selectedBrick = [];
30910 this.currentSize = this.el.getBox(true);
30912 Roo.EventManager.onWindowResize(this.resize, this);
30914 if(!this.isAutoInitial){
30922 //this.layout.defer(500,this);
30926 resize : function()
30928 var cs = this.el.getBox(true);
30931 this.currentSize.width == cs.width &&
30932 this.currentSize.x == cs.x &&
30933 this.currentSize.height == cs.height &&
30934 this.currentSize.y == cs.y
30936 Roo.log("no change in with or X or Y");
30940 this.currentSize = cs;
30946 layout : function()
30948 this._resetLayout();
30950 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30952 this.layoutItems( isInstant );
30954 this._isLayoutInited = true;
30956 this.fireEvent('layout', this);
30960 _resetLayout : function()
30962 if(this.isHorizontal){
30963 this.horizontalMeasureColumns();
30967 this.verticalMeasureColumns();
30971 verticalMeasureColumns : function()
30973 this.getContainerWidth();
30975 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30976 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30980 var boxWidth = this.boxWidth + this.padWidth;
30982 if(this.containerWidth < this.boxWidth){
30983 boxWidth = this.containerWidth
30986 var containerWidth = this.containerWidth;
30988 var cols = Math.floor(containerWidth / boxWidth);
30990 this.cols = Math.max( cols, 1 );
30992 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30994 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30996 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30998 this.colWidth = boxWidth + avail - this.padWidth;
31000 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31001 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31004 horizontalMeasureColumns : function()
31006 this.getContainerWidth();
31008 var boxWidth = this.boxWidth;
31010 if(this.containerWidth < boxWidth){
31011 boxWidth = this.containerWidth;
31014 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31016 this.el.setHeight(boxWidth);
31020 getContainerWidth : function()
31022 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31025 layoutItems : function( isInstant )
31027 Roo.log(this.bricks);
31029 var items = Roo.apply([], this.bricks);
31031 if(this.isHorizontal){
31032 this._horizontalLayoutItems( items , isInstant );
31036 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31037 // this._verticalAlternativeLayoutItems( items , isInstant );
31041 this._verticalLayoutItems( items , isInstant );
31045 _verticalLayoutItems : function ( items , isInstant)
31047 if ( !items || !items.length ) {
31052 ['xs', 'xs', 'xs', 'tall'],
31053 ['xs', 'xs', 'tall'],
31054 ['xs', 'xs', 'sm'],
31055 ['xs', 'xs', 'xs'],
31061 ['sm', 'xs', 'xs'],
31065 ['tall', 'xs', 'xs', 'xs'],
31066 ['tall', 'xs', 'xs'],
31078 Roo.each(items, function(item, k){
31080 switch (item.size) {
31081 // these layouts take up a full box,
31092 boxes.push([item]);
31115 var filterPattern = function(box, length)
31123 var pattern = box.slice(0, length);
31127 Roo.each(pattern, function(i){
31128 format.push(i.size);
31131 Roo.each(standard, function(s){
31133 if(String(s) != String(format)){
31142 if(!match && length == 1){
31147 filterPattern(box, length - 1);
31151 queue.push(pattern);
31153 box = box.slice(length, box.length);
31155 filterPattern(box, 4);
31161 Roo.each(boxes, function(box, k){
31167 if(box.length == 1){
31172 filterPattern(box, 4);
31176 this._processVerticalLayoutQueue( queue, isInstant );
31180 // _verticalAlternativeLayoutItems : function( items , isInstant )
31182 // if ( !items || !items.length ) {
31186 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31190 _horizontalLayoutItems : function ( items , isInstant)
31192 if ( !items || !items.length || items.length < 3) {
31198 var eItems = items.slice(0, 3);
31200 items = items.slice(3, items.length);
31203 ['xs', 'xs', 'xs', 'wide'],
31204 ['xs', 'xs', 'wide'],
31205 ['xs', 'xs', 'sm'],
31206 ['xs', 'xs', 'xs'],
31212 ['sm', 'xs', 'xs'],
31216 ['wide', 'xs', 'xs', 'xs'],
31217 ['wide', 'xs', 'xs'],
31230 Roo.each(items, function(item, k){
31232 switch (item.size) {
31243 boxes.push([item]);
31267 var filterPattern = function(box, length)
31275 var pattern = box.slice(0, length);
31279 Roo.each(pattern, function(i){
31280 format.push(i.size);
31283 Roo.each(standard, function(s){
31285 if(String(s) != String(format)){
31294 if(!match && length == 1){
31299 filterPattern(box, length - 1);
31303 queue.push(pattern);
31305 box = box.slice(length, box.length);
31307 filterPattern(box, 4);
31313 Roo.each(boxes, function(box, k){
31319 if(box.length == 1){
31324 filterPattern(box, 4);
31331 var pos = this.el.getBox(true);
31335 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31337 var hit_end = false;
31339 Roo.each(queue, function(box){
31343 Roo.each(box, function(b){
31345 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31355 Roo.each(box, function(b){
31357 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31360 mx = Math.max(mx, b.x);
31364 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31368 Roo.each(box, function(b){
31370 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31384 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31387 /** Sets position of item in DOM
31388 * @param {Element} item
31389 * @param {Number} x - horizontal position
31390 * @param {Number} y - vertical position
31391 * @param {Boolean} isInstant - disables transitions
31393 _processVerticalLayoutQueue : function( queue, isInstant )
31395 var pos = this.el.getBox(true);
31400 for (var i = 0; i < this.cols; i++){
31404 Roo.each(queue, function(box, k){
31406 var col = k % this.cols;
31408 Roo.each(box, function(b,kk){
31410 b.el.position('absolute');
31412 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31413 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31415 if(b.size == 'md-left' || b.size == 'md-right'){
31416 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31417 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31420 b.el.setWidth(width);
31421 b.el.setHeight(height);
31423 b.el.select('iframe',true).setSize(width,height);
31427 for (var i = 0; i < this.cols; i++){
31429 if(maxY[i] < maxY[col]){
31434 col = Math.min(col, i);
31438 x = pos.x + col * (this.colWidth + this.padWidth);
31442 var positions = [];
31444 switch (box.length){
31446 positions = this.getVerticalOneBoxColPositions(x, y, box);
31449 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31452 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31455 positions = this.getVerticalFourBoxColPositions(x, y, box);
31461 Roo.each(box, function(b,kk){
31463 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31465 var sz = b.el.getSize();
31467 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31475 for (var i = 0; i < this.cols; i++){
31476 mY = Math.max(mY, maxY[i]);
31479 this.el.setHeight(mY - pos.y);
31483 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31485 // var pos = this.el.getBox(true);
31488 // var maxX = pos.right;
31490 // var maxHeight = 0;
31492 // Roo.each(items, function(item, k){
31496 // item.el.position('absolute');
31498 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31500 // item.el.setWidth(width);
31502 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31504 // item.el.setHeight(height);
31507 // item.el.setXY([x, y], isInstant ? false : true);
31509 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31512 // y = y + height + this.alternativePadWidth;
31514 // maxHeight = maxHeight + height + this.alternativePadWidth;
31518 // this.el.setHeight(maxHeight);
31522 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31524 var pos = this.el.getBox(true);
31529 var maxX = pos.right;
31531 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31533 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31535 Roo.each(queue, function(box, k){
31537 Roo.each(box, function(b, kk){
31539 b.el.position('absolute');
31541 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31542 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31544 if(b.size == 'md-left' || b.size == 'md-right'){
31545 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31546 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31549 b.el.setWidth(width);
31550 b.el.setHeight(height);
31558 var positions = [];
31560 switch (box.length){
31562 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31565 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31568 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31571 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31577 Roo.each(box, function(b,kk){
31579 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31581 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31589 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31591 Roo.each(eItems, function(b,k){
31593 b.size = (k == 0) ? 'sm' : 'xs';
31594 b.x = (k == 0) ? 2 : 1;
31595 b.y = (k == 0) ? 2 : 1;
31597 b.el.position('absolute');
31599 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31601 b.el.setWidth(width);
31603 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31605 b.el.setHeight(height);
31609 var positions = [];
31612 x : maxX - this.unitWidth * 2 - this.gutter,
31617 x : maxX - this.unitWidth,
31618 y : minY + (this.unitWidth + this.gutter) * 2
31622 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31626 Roo.each(eItems, function(b,k){
31628 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31634 getVerticalOneBoxColPositions : function(x, y, box)
31638 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31640 if(box[0].size == 'md-left'){
31644 if(box[0].size == 'md-right'){
31649 x : x + (this.unitWidth + this.gutter) * rand,
31656 getVerticalTwoBoxColPositions : function(x, y, box)
31660 if(box[0].size == 'xs'){
31664 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31668 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31682 x : x + (this.unitWidth + this.gutter) * 2,
31683 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31690 getVerticalThreeBoxColPositions : function(x, y, box)
31694 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31702 x : x + (this.unitWidth + this.gutter) * 1,
31707 x : x + (this.unitWidth + this.gutter) * 2,
31715 if(box[0].size == 'xs' && box[1].size == 'xs'){
31724 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31728 x : x + (this.unitWidth + this.gutter) * 1,
31742 x : x + (this.unitWidth + this.gutter) * 2,
31747 x : x + (this.unitWidth + this.gutter) * 2,
31748 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31755 getVerticalFourBoxColPositions : function(x, y, box)
31759 if(box[0].size == 'xs'){
31768 y : y + (this.unitHeight + this.gutter) * 1
31773 y : y + (this.unitHeight + this.gutter) * 2
31777 x : x + (this.unitWidth + this.gutter) * 1,
31791 x : x + (this.unitWidth + this.gutter) * 2,
31796 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31797 y : y + (this.unitHeight + this.gutter) * 1
31801 x : x + (this.unitWidth + this.gutter) * 2,
31802 y : y + (this.unitWidth + this.gutter) * 2
31809 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31813 if(box[0].size == 'md-left'){
31815 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31822 if(box[0].size == 'md-right'){
31824 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31825 y : minY + (this.unitWidth + this.gutter) * 1
31831 var rand = Math.floor(Math.random() * (4 - box[0].y));
31834 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31835 y : minY + (this.unitWidth + this.gutter) * rand
31842 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31846 if(box[0].size == 'xs'){
31849 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31854 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31855 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31863 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31868 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31869 y : minY + (this.unitWidth + this.gutter) * 2
31876 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31880 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31883 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31888 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31889 y : minY + (this.unitWidth + this.gutter) * 1
31893 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31894 y : minY + (this.unitWidth + this.gutter) * 2
31901 if(box[0].size == 'xs' && box[1].size == 'xs'){
31904 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31909 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31914 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31915 y : minY + (this.unitWidth + this.gutter) * 1
31923 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31928 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31929 y : minY + (this.unitWidth + this.gutter) * 2
31933 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31934 y : minY + (this.unitWidth + this.gutter) * 2
31941 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31945 if(box[0].size == 'xs'){
31948 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31953 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31958 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),
31963 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31964 y : minY + (this.unitWidth + this.gutter) * 1
31972 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31977 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31978 y : minY + (this.unitWidth + this.gutter) * 2
31982 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31983 y : minY + (this.unitWidth + this.gutter) * 2
31987 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),
31988 y : minY + (this.unitWidth + this.gutter) * 2
31996 * remove a Masonry Brick
31997 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31999 removeBrick : function(brick_id)
32005 for (var i = 0; i<this.bricks.length; i++) {
32006 if (this.bricks[i].id == brick_id) {
32007 this.bricks.splice(i,1);
32008 this.el.dom.removeChild(Roo.get(brick_id).dom);
32015 * adds a Masonry Brick
32016 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32018 addBrick : function(cfg)
32020 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32021 //this.register(cn);
32022 cn.parentId = this.id;
32023 cn.render(this.el);
32028 * register a Masonry Brick
32029 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32032 register : function(brick)
32034 this.bricks.push(brick);
32035 brick.masonryId = this.id;
32039 * clear all the Masonry Brick
32041 clearAll : function()
32044 //this.getChildContainer().dom.innerHTML = "";
32045 this.el.dom.innerHTML = '';
32048 getSelected : function()
32050 if (!this.selectedBrick) {
32054 return this.selectedBrick;
32058 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32062 * register a Masonry Layout
32063 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32066 register : function(layout)
32068 this.groups[layout.id] = layout;
32071 * fetch a Masonry Layout based on the masonry layout ID
32072 * @param {string} the masonry layout to add
32073 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32076 get: function(layout_id) {
32077 if (typeof(this.groups[layout_id]) == 'undefined') {
32080 return this.groups[layout_id] ;
32092 * http://masonry.desandro.com
32094 * The idea is to render all the bricks based on vertical width...
32096 * The original code extends 'outlayer' - we might need to use that....
32102 * @class Roo.bootstrap.LayoutMasonryAuto
32103 * @extends Roo.bootstrap.Component
32104 * Bootstrap Layout Masonry class
32107 * Create a new Element
32108 * @param {Object} config The config object
32111 Roo.bootstrap.LayoutMasonryAuto = function(config){
32112 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32115 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32118 * @cfg {Boolean} isFitWidth - resize the width..
32120 isFitWidth : false, // options..
32122 * @cfg {Boolean} isOriginLeft = left align?
32124 isOriginLeft : true,
32126 * @cfg {Boolean} isOriginTop = top align?
32128 isOriginTop : false,
32130 * @cfg {Boolean} isLayoutInstant = no animation?
32132 isLayoutInstant : false, // needed?
32134 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32136 isResizingContainer : true,
32138 * @cfg {Number} columnWidth width of the columns
32144 * @cfg {Number} maxCols maximum number of columns
32149 * @cfg {Number} padHeight padding below box..
32155 * @cfg {Boolean} isAutoInitial defalut true
32158 isAutoInitial : true,
32164 initialColumnWidth : 0,
32165 currentSize : null,
32167 colYs : null, // array.
32174 bricks: null, //CompositeElement
32175 cols : 0, // array?
32176 // element : null, // wrapped now this.el
32177 _isLayoutInited : null,
32180 getAutoCreate : function(){
32184 cls: 'blog-masonary-wrapper ' + this.cls,
32186 cls : 'mas-boxes masonary'
32193 getChildContainer: function( )
32195 if (this.boxesEl) {
32196 return this.boxesEl;
32199 this.boxesEl = this.el.select('.mas-boxes').first();
32201 return this.boxesEl;
32205 initEvents : function()
32209 if(this.isAutoInitial){
32210 Roo.log('hook children rendered');
32211 this.on('childrenrendered', function() {
32212 Roo.log('children rendered');
32219 initial : function()
32221 this.reloadItems();
32223 this.currentSize = this.el.getBox(true);
32225 /// was window resize... - let's see if this works..
32226 Roo.EventManager.onWindowResize(this.resize, this);
32228 if(!this.isAutoInitial){
32233 this.layout.defer(500,this);
32236 reloadItems: function()
32238 this.bricks = this.el.select('.masonry-brick', true);
32240 this.bricks.each(function(b) {
32241 //Roo.log(b.getSize());
32242 if (!b.attr('originalwidth')) {
32243 b.attr('originalwidth', b.getSize().width);
32248 Roo.log(this.bricks.elements.length);
32251 resize : function()
32254 var cs = this.el.getBox(true);
32256 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32257 Roo.log("no change in with or X");
32260 this.currentSize = cs;
32264 layout : function()
32267 this._resetLayout();
32268 //this._manageStamps();
32270 // don't animate first layout
32271 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32272 this.layoutItems( isInstant );
32274 // flag for initalized
32275 this._isLayoutInited = true;
32278 layoutItems : function( isInstant )
32280 //var items = this._getItemsForLayout( this.items );
32281 // original code supports filtering layout items.. we just ignore it..
32283 this._layoutItems( this.bricks , isInstant );
32285 this._postLayout();
32287 _layoutItems : function ( items , isInstant)
32289 //this.fireEvent( 'layout', this, items );
32292 if ( !items || !items.elements.length ) {
32293 // no items, emit event with empty array
32298 items.each(function(item) {
32299 Roo.log("layout item");
32301 // get x/y object from method
32302 var position = this._getItemLayoutPosition( item );
32304 position.item = item;
32305 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32306 queue.push( position );
32309 this._processLayoutQueue( queue );
32311 /** Sets position of item in DOM
32312 * @param {Element} item
32313 * @param {Number} x - horizontal position
32314 * @param {Number} y - vertical position
32315 * @param {Boolean} isInstant - disables transitions
32317 _processLayoutQueue : function( queue )
32319 for ( var i=0, len = queue.length; i < len; i++ ) {
32320 var obj = queue[i];
32321 obj.item.position('absolute');
32322 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32328 * Any logic you want to do after each layout,
32329 * i.e. size the container
32331 _postLayout : function()
32333 this.resizeContainer();
32336 resizeContainer : function()
32338 if ( !this.isResizingContainer ) {
32341 var size = this._getContainerSize();
32343 this.el.setSize(size.width,size.height);
32344 this.boxesEl.setSize(size.width,size.height);
32350 _resetLayout : function()
32352 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32353 this.colWidth = this.el.getWidth();
32354 //this.gutter = this.el.getWidth();
32356 this.measureColumns();
32362 this.colYs.push( 0 );
32368 measureColumns : function()
32370 this.getContainerWidth();
32371 // if columnWidth is 0, default to outerWidth of first item
32372 if ( !this.columnWidth ) {
32373 var firstItem = this.bricks.first();
32374 Roo.log(firstItem);
32375 this.columnWidth = this.containerWidth;
32376 if (firstItem && firstItem.attr('originalwidth') ) {
32377 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32379 // columnWidth fall back to item of first element
32380 Roo.log("set column width?");
32381 this.initialColumnWidth = this.columnWidth ;
32383 // if first elem has no width, default to size of container
32388 if (this.initialColumnWidth) {
32389 this.columnWidth = this.initialColumnWidth;
32394 // column width is fixed at the top - however if container width get's smaller we should
32397 // this bit calcs how man columns..
32399 var columnWidth = this.columnWidth += this.gutter;
32401 // calculate columns
32402 var containerWidth = this.containerWidth + this.gutter;
32404 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32405 // fix rounding errors, typically with gutters
32406 var excess = columnWidth - containerWidth % columnWidth;
32409 // if overshoot is less than a pixel, round up, otherwise floor it
32410 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32411 cols = Math[ mathMethod ]( cols );
32412 this.cols = Math.max( cols, 1 );
32413 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32415 // padding positioning..
32416 var totalColWidth = this.cols * this.columnWidth;
32417 var padavail = this.containerWidth - totalColWidth;
32418 // so for 2 columns - we need 3 'pads'
32420 var padNeeded = (1+this.cols) * this.padWidth;
32422 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32424 this.columnWidth += padExtra
32425 //this.padWidth = Math.floor(padavail / ( this.cols));
32427 // adjust colum width so that padding is fixed??
32429 // we have 3 columns ... total = width * 3
32430 // we have X left over... that should be used by
32432 //if (this.expandC) {
32440 getContainerWidth : function()
32442 /* // container is parent if fit width
32443 var container = this.isFitWidth ? this.element.parentNode : this.element;
32444 // check that this.size and size are there
32445 // IE8 triggers resize on body size change, so they might not be
32447 var size = getSize( container ); //FIXME
32448 this.containerWidth = size && size.innerWidth; //FIXME
32451 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32455 _getItemLayoutPosition : function( item ) // what is item?
32457 // we resize the item to our columnWidth..
32459 item.setWidth(this.columnWidth);
32460 item.autoBoxAdjust = false;
32462 var sz = item.getSize();
32464 // how many columns does this brick span
32465 var remainder = this.containerWidth % this.columnWidth;
32467 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32468 // round if off by 1 pixel, otherwise use ceil
32469 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32470 colSpan = Math.min( colSpan, this.cols );
32472 // normally this should be '1' as we dont' currently allow multi width columns..
32474 var colGroup = this._getColGroup( colSpan );
32475 // get the minimum Y value from the columns
32476 var minimumY = Math.min.apply( Math, colGroup );
32477 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32479 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32481 // position the brick
32483 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32484 y: this.currentSize.y + minimumY + this.padHeight
32488 // apply setHeight to necessary columns
32489 var setHeight = minimumY + sz.height + this.padHeight;
32490 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32492 var setSpan = this.cols + 1 - colGroup.length;
32493 for ( var i = 0; i < setSpan; i++ ) {
32494 this.colYs[ shortColIndex + i ] = setHeight ;
32501 * @param {Number} colSpan - number of columns the element spans
32502 * @returns {Array} colGroup
32504 _getColGroup : function( colSpan )
32506 if ( colSpan < 2 ) {
32507 // if brick spans only one column, use all the column Ys
32512 // how many different places could this brick fit horizontally
32513 var groupCount = this.cols + 1 - colSpan;
32514 // for each group potential horizontal position
32515 for ( var i = 0; i < groupCount; i++ ) {
32516 // make an array of colY values for that one group
32517 var groupColYs = this.colYs.slice( i, i + colSpan );
32518 // and get the max value of the array
32519 colGroup[i] = Math.max.apply( Math, groupColYs );
32524 _manageStamp : function( stamp )
32526 var stampSize = stamp.getSize();
32527 var offset = stamp.getBox();
32528 // get the columns that this stamp affects
32529 var firstX = this.isOriginLeft ? offset.x : offset.right;
32530 var lastX = firstX + stampSize.width;
32531 var firstCol = Math.floor( firstX / this.columnWidth );
32532 firstCol = Math.max( 0, firstCol );
32534 var lastCol = Math.floor( lastX / this.columnWidth );
32535 // lastCol should not go over if multiple of columnWidth #425
32536 lastCol -= lastX % this.columnWidth ? 0 : 1;
32537 lastCol = Math.min( this.cols - 1, lastCol );
32539 // set colYs to bottom of the stamp
32540 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32543 for ( var i = firstCol; i <= lastCol; i++ ) {
32544 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32549 _getContainerSize : function()
32551 this.maxY = Math.max.apply( Math, this.colYs );
32556 if ( this.isFitWidth ) {
32557 size.width = this._getContainerFitWidth();
32563 _getContainerFitWidth : function()
32565 var unusedCols = 0;
32566 // count unused columns
32569 if ( this.colYs[i] !== 0 ) {
32574 // fit container to columns that have been used
32575 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32578 needsResizeLayout : function()
32580 var previousWidth = this.containerWidth;
32581 this.getContainerWidth();
32582 return previousWidth !== this.containerWidth;
32597 * @class Roo.bootstrap.MasonryBrick
32598 * @extends Roo.bootstrap.Component
32599 * Bootstrap MasonryBrick class
32602 * Create a new MasonryBrick
32603 * @param {Object} config The config object
32606 Roo.bootstrap.MasonryBrick = function(config){
32608 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32610 Roo.bootstrap.MasonryBrick.register(this);
32616 * When a MasonryBrick is clcik
32617 * @param {Roo.bootstrap.MasonryBrick} this
32618 * @param {Roo.EventObject} e
32624 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32627 * @cfg {String} title
32631 * @cfg {String} html
32635 * @cfg {String} bgimage
32639 * @cfg {String} videourl
32643 * @cfg {String} cls
32647 * @cfg {String} href
32651 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32656 * @cfg {String} placetitle (center|bottom)
32661 * @cfg {Boolean} isFitContainer defalut true
32663 isFitContainer : true,
32666 * @cfg {Boolean} preventDefault defalut false
32668 preventDefault : false,
32671 * @cfg {Boolean} inverse defalut false
32673 maskInverse : false,
32675 getAutoCreate : function()
32677 if(!this.isFitContainer){
32678 return this.getSplitAutoCreate();
32681 var cls = 'masonry-brick masonry-brick-full';
32683 if(this.href.length){
32684 cls += ' masonry-brick-link';
32687 if(this.bgimage.length){
32688 cls += ' masonry-brick-image';
32691 if(this.maskInverse){
32692 cls += ' mask-inverse';
32695 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32696 cls += ' enable-mask';
32700 cls += ' masonry-' + this.size + '-brick';
32703 if(this.placetitle.length){
32705 switch (this.placetitle) {
32707 cls += ' masonry-center-title';
32710 cls += ' masonry-bottom-title';
32717 if(!this.html.length && !this.bgimage.length){
32718 cls += ' masonry-center-title';
32721 if(!this.html.length && this.bgimage.length){
32722 cls += ' masonry-bottom-title';
32727 cls += ' ' + this.cls;
32731 tag: (this.href.length) ? 'a' : 'div',
32736 cls: 'masonry-brick-mask'
32740 cls: 'masonry-brick-paragraph',
32746 if(this.href.length){
32747 cfg.href = this.href;
32750 var cn = cfg.cn[1].cn;
32752 if(this.title.length){
32755 cls: 'masonry-brick-title',
32760 if(this.html.length){
32763 cls: 'masonry-brick-text',
32768 if (!this.title.length && !this.html.length) {
32769 cfg.cn[1].cls += ' hide';
32772 if(this.bgimage.length){
32775 cls: 'masonry-brick-image-view',
32780 if(this.videourl.length){
32781 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32782 // youtube support only?
32785 cls: 'masonry-brick-image-view',
32788 allowfullscreen : true
32796 getSplitAutoCreate : function()
32798 var cls = 'masonry-brick masonry-brick-split';
32800 if(this.href.length){
32801 cls += ' masonry-brick-link';
32804 if(this.bgimage.length){
32805 cls += ' masonry-brick-image';
32809 cls += ' masonry-' + this.size + '-brick';
32812 switch (this.placetitle) {
32814 cls += ' masonry-center-title';
32817 cls += ' masonry-bottom-title';
32820 if(!this.bgimage.length){
32821 cls += ' masonry-center-title';
32824 if(this.bgimage.length){
32825 cls += ' masonry-bottom-title';
32831 cls += ' ' + this.cls;
32835 tag: (this.href.length) ? 'a' : 'div',
32840 cls: 'masonry-brick-split-head',
32844 cls: 'masonry-brick-paragraph',
32851 cls: 'masonry-brick-split-body',
32857 if(this.href.length){
32858 cfg.href = this.href;
32861 if(this.title.length){
32862 cfg.cn[0].cn[0].cn.push({
32864 cls: 'masonry-brick-title',
32869 if(this.html.length){
32870 cfg.cn[1].cn.push({
32872 cls: 'masonry-brick-text',
32877 if(this.bgimage.length){
32878 cfg.cn[0].cn.push({
32880 cls: 'masonry-brick-image-view',
32885 if(this.videourl.length){
32886 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32887 // youtube support only?
32888 cfg.cn[0].cn.cn.push({
32890 cls: 'masonry-brick-image-view',
32893 allowfullscreen : true
32900 initEvents: function()
32902 switch (this.size) {
32935 this.el.on('touchstart', this.onTouchStart, this);
32936 this.el.on('touchmove', this.onTouchMove, this);
32937 this.el.on('touchend', this.onTouchEnd, this);
32938 this.el.on('contextmenu', this.onContextMenu, this);
32940 this.el.on('mouseenter' ,this.enter, this);
32941 this.el.on('mouseleave', this.leave, this);
32942 this.el.on('click', this.onClick, this);
32945 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32946 this.parent().bricks.push(this);
32951 onClick: function(e, el)
32953 var time = this.endTimer - this.startTimer;
32954 // Roo.log(e.preventDefault());
32957 e.preventDefault();
32962 if(!this.preventDefault){
32966 e.preventDefault();
32968 if (this.activeClass != '') {
32969 this.selectBrick();
32972 this.fireEvent('click', this, e);
32975 enter: function(e, el)
32977 e.preventDefault();
32979 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32983 if(this.bgimage.length && this.html.length){
32984 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32988 leave: function(e, el)
32990 e.preventDefault();
32992 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32996 if(this.bgimage.length && this.html.length){
32997 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33001 onTouchStart: function(e, el)
33003 // e.preventDefault();
33005 this.touchmoved = false;
33007 if(!this.isFitContainer){
33011 if(!this.bgimage.length || !this.html.length){
33015 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33017 this.timer = new Date().getTime();
33021 onTouchMove: function(e, el)
33023 this.touchmoved = true;
33026 onContextMenu : function(e,el)
33028 e.preventDefault();
33029 e.stopPropagation();
33033 onTouchEnd: function(e, el)
33035 // e.preventDefault();
33037 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33044 if(!this.bgimage.length || !this.html.length){
33046 if(this.href.length){
33047 window.location.href = this.href;
33053 if(!this.isFitContainer){
33057 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33059 window.location.href = this.href;
33062 //selection on single brick only
33063 selectBrick : function() {
33065 if (!this.parentId) {
33069 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33070 var index = m.selectedBrick.indexOf(this.id);
33073 m.selectedBrick.splice(index,1);
33074 this.el.removeClass(this.activeClass);
33078 for(var i = 0; i < m.selectedBrick.length; i++) {
33079 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33080 b.el.removeClass(b.activeClass);
33083 m.selectedBrick = [];
33085 m.selectedBrick.push(this.id);
33086 this.el.addClass(this.activeClass);
33090 isSelected : function(){
33091 return this.el.hasClass(this.activeClass);
33096 Roo.apply(Roo.bootstrap.MasonryBrick, {
33099 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33101 * register a Masonry Brick
33102 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33105 register : function(brick)
33107 //this.groups[brick.id] = brick;
33108 this.groups.add(brick.id, brick);
33111 * fetch a masonry brick based on the masonry brick ID
33112 * @param {string} the masonry brick to add
33113 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33116 get: function(brick_id)
33118 // if (typeof(this.groups[brick_id]) == 'undefined') {
33121 // return this.groups[brick_id] ;
33123 if(this.groups.key(brick_id)) {
33124 return this.groups.key(brick_id);
33142 * @class Roo.bootstrap.Brick
33143 * @extends Roo.bootstrap.Component
33144 * Bootstrap Brick class
33147 * Create a new Brick
33148 * @param {Object} config The config object
33151 Roo.bootstrap.Brick = function(config){
33152 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33158 * When a Brick is click
33159 * @param {Roo.bootstrap.Brick} this
33160 * @param {Roo.EventObject} e
33166 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33169 * @cfg {String} title
33173 * @cfg {String} html
33177 * @cfg {String} bgimage
33181 * @cfg {String} cls
33185 * @cfg {String} href
33189 * @cfg {String} video
33193 * @cfg {Boolean} square
33197 getAutoCreate : function()
33199 var cls = 'roo-brick';
33201 if(this.href.length){
33202 cls += ' roo-brick-link';
33205 if(this.bgimage.length){
33206 cls += ' roo-brick-image';
33209 if(!this.html.length && !this.bgimage.length){
33210 cls += ' roo-brick-center-title';
33213 if(!this.html.length && this.bgimage.length){
33214 cls += ' roo-brick-bottom-title';
33218 cls += ' ' + this.cls;
33222 tag: (this.href.length) ? 'a' : 'div',
33227 cls: 'roo-brick-paragraph',
33233 if(this.href.length){
33234 cfg.href = this.href;
33237 var cn = cfg.cn[0].cn;
33239 if(this.title.length){
33242 cls: 'roo-brick-title',
33247 if(this.html.length){
33250 cls: 'roo-brick-text',
33257 if(this.bgimage.length){
33260 cls: 'roo-brick-image-view',
33268 initEvents: function()
33270 if(this.title.length || this.html.length){
33271 this.el.on('mouseenter' ,this.enter, this);
33272 this.el.on('mouseleave', this.leave, this);
33275 Roo.EventManager.onWindowResize(this.resize, this);
33277 if(this.bgimage.length){
33278 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33279 this.imageEl.on('load', this.onImageLoad, this);
33286 onImageLoad : function()
33291 resize : function()
33293 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33295 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33297 if(this.bgimage.length){
33298 var image = this.el.select('.roo-brick-image-view', true).first();
33300 image.setWidth(paragraph.getWidth());
33303 image.setHeight(paragraph.getWidth());
33306 this.el.setHeight(image.getHeight());
33307 paragraph.setHeight(image.getHeight());
33313 enter: function(e, el)
33315 e.preventDefault();
33317 if(this.bgimage.length){
33318 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33319 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33323 leave: function(e, el)
33325 e.preventDefault();
33327 if(this.bgimage.length){
33328 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33329 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33344 * @class Roo.bootstrap.NumberField
33345 * @extends Roo.bootstrap.Input
33346 * Bootstrap NumberField class
33352 * Create a new NumberField
33353 * @param {Object} config The config object
33356 Roo.bootstrap.NumberField = function(config){
33357 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33360 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33363 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33365 allowDecimals : true,
33367 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33369 decimalSeparator : ".",
33371 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33373 decimalPrecision : 2,
33375 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33377 allowNegative : true,
33380 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33384 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33386 minValue : Number.NEGATIVE_INFINITY,
33388 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33390 maxValue : Number.MAX_VALUE,
33392 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33394 minText : "The minimum value for this field is {0}",
33396 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33398 maxText : "The maximum value for this field is {0}",
33400 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33401 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33403 nanText : "{0} is not a valid number",
33405 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33407 thousandsDelimiter : false,
33409 * @cfg {String} valueAlign alignment of value
33411 valueAlign : "left",
33413 getAutoCreate : function()
33415 var hiddenInput = {
33419 cls: 'hidden-number-input'
33423 hiddenInput.name = this.name;
33428 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33430 this.name = hiddenInput.name;
33432 if(cfg.cn.length > 0) {
33433 cfg.cn.push(hiddenInput);
33440 initEvents : function()
33442 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33444 var allowed = "0123456789";
33446 if(this.allowDecimals){
33447 allowed += this.decimalSeparator;
33450 if(this.allowNegative){
33454 if(this.thousandsDelimiter) {
33458 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33460 var keyPress = function(e){
33462 var k = e.getKey();
33464 var c = e.getCharCode();
33467 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33468 allowed.indexOf(String.fromCharCode(c)) === -1
33474 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33478 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33483 this.el.on("keypress", keyPress, this);
33486 validateValue : function(value)
33489 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33493 var num = this.parseValue(value);
33496 this.markInvalid(String.format(this.nanText, value));
33500 if(num < this.minValue){
33501 this.markInvalid(String.format(this.minText, this.minValue));
33505 if(num > this.maxValue){
33506 this.markInvalid(String.format(this.maxText, this.maxValue));
33513 getValue : function()
33515 var v = this.hiddenEl().getValue();
33517 return this.fixPrecision(this.parseValue(v));
33520 parseValue : function(value)
33522 if(this.thousandsDelimiter) {
33524 r = new RegExp(",", "g");
33525 value = value.replace(r, "");
33528 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33529 return isNaN(value) ? '' : value;
33532 fixPrecision : function(value)
33534 if(this.thousandsDelimiter) {
33536 r = new RegExp(",", "g");
33537 value = value.replace(r, "");
33540 var nan = isNaN(value);
33542 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33543 return nan ? '' : value;
33545 return parseFloat(value).toFixed(this.decimalPrecision);
33548 setValue : function(v)
33550 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33556 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33558 this.inputEl().dom.value = (v == '') ? '' :
33559 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33561 if(!this.allowZero && v === '0') {
33562 this.hiddenEl().dom.value = '';
33563 this.inputEl().dom.value = '';
33570 decimalPrecisionFcn : function(v)
33572 return Math.floor(v);
33575 beforeBlur : function()
33577 var v = this.parseValue(this.getRawValue());
33579 if(v || v === 0 || v === ''){
33584 hiddenEl : function()
33586 return this.el.select('input.hidden-number-input',true).first();
33598 * @class Roo.bootstrap.DocumentSlider
33599 * @extends Roo.bootstrap.Component
33600 * Bootstrap DocumentSlider class
33603 * Create a new DocumentViewer
33604 * @param {Object} config The config object
33607 Roo.bootstrap.DocumentSlider = function(config){
33608 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33615 * Fire after initEvent
33616 * @param {Roo.bootstrap.DocumentSlider} this
33621 * Fire after update
33622 * @param {Roo.bootstrap.DocumentSlider} this
33628 * @param {Roo.bootstrap.DocumentSlider} this
33634 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33640 getAutoCreate : function()
33644 cls : 'roo-document-slider',
33648 cls : 'roo-document-slider-header',
33652 cls : 'roo-document-slider-header-title'
33658 cls : 'roo-document-slider-body',
33662 cls : 'roo-document-slider-prev',
33666 cls : 'fa fa-chevron-left'
33672 cls : 'roo-document-slider-thumb',
33676 cls : 'roo-document-slider-image'
33682 cls : 'roo-document-slider-next',
33686 cls : 'fa fa-chevron-right'
33698 initEvents : function()
33700 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33701 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33703 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33704 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33706 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33707 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33709 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33710 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33712 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33713 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33715 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33716 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33718 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33719 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33721 this.thumbEl.on('click', this.onClick, this);
33723 this.prevIndicator.on('click', this.prev, this);
33725 this.nextIndicator.on('click', this.next, this);
33729 initial : function()
33731 if(this.files.length){
33732 this.indicator = 1;
33736 this.fireEvent('initial', this);
33739 update : function()
33741 this.imageEl.attr('src', this.files[this.indicator - 1]);
33743 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33745 this.prevIndicator.show();
33747 if(this.indicator == 1){
33748 this.prevIndicator.hide();
33751 this.nextIndicator.show();
33753 if(this.indicator == this.files.length){
33754 this.nextIndicator.hide();
33757 this.thumbEl.scrollTo('top');
33759 this.fireEvent('update', this);
33762 onClick : function(e)
33764 e.preventDefault();
33766 this.fireEvent('click', this);
33771 e.preventDefault();
33773 this.indicator = Math.max(1, this.indicator - 1);
33780 e.preventDefault();
33782 this.indicator = Math.min(this.files.length, this.indicator + 1);
33796 * @class Roo.bootstrap.RadioSet
33797 * @extends Roo.bootstrap.Input
33798 * Bootstrap RadioSet class
33799 * @cfg {String} indicatorpos (left|right) default left
33800 * @cfg {Boolean} inline (true|false) inline the element (default true)
33801 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33803 * Create a new RadioSet
33804 * @param {Object} config The config object
33807 Roo.bootstrap.RadioSet = function(config){
33809 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33813 Roo.bootstrap.RadioSet.register(this);
33818 * Fires when the element is checked or unchecked.
33819 * @param {Roo.bootstrap.RadioSet} this This radio
33820 * @param {Roo.bootstrap.Radio} item The checked item
33825 * Fires when the element is click.
33826 * @param {Roo.bootstrap.RadioSet} this This radio set
33827 * @param {Roo.bootstrap.Radio} item The checked item
33828 * @param {Roo.EventObject} e The event object
33835 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33843 indicatorpos : 'left',
33845 getAutoCreate : function()
33849 cls : 'roo-radio-set-label',
33853 html : this.fieldLabel
33858 if(this.indicatorpos == 'left'){
33861 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33862 tooltip : 'This field is required'
33867 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33868 tooltip : 'This field is required'
33874 cls : 'roo-radio-set-items'
33877 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33879 if (align === 'left' && this.fieldLabel.length) {
33882 cls : "roo-radio-set-right",
33888 if(this.labelWidth > 12){
33889 label.style = "width: " + this.labelWidth + 'px';
33892 if(this.labelWidth < 13 && this.labelmd == 0){
33893 this.labelmd = this.labelWidth;
33896 if(this.labellg > 0){
33897 label.cls += ' col-lg-' + this.labellg;
33898 items.cls += ' col-lg-' + (12 - this.labellg);
33901 if(this.labelmd > 0){
33902 label.cls += ' col-md-' + this.labelmd;
33903 items.cls += ' col-md-' + (12 - this.labelmd);
33906 if(this.labelsm > 0){
33907 label.cls += ' col-sm-' + this.labelsm;
33908 items.cls += ' col-sm-' + (12 - this.labelsm);
33911 if(this.labelxs > 0){
33912 label.cls += ' col-xs-' + this.labelxs;
33913 items.cls += ' col-xs-' + (12 - this.labelxs);
33919 cls : 'roo-radio-set',
33923 cls : 'roo-radio-set-input',
33926 value : this.value ? this.value : ''
33933 if(this.weight.length){
33934 cfg.cls += ' roo-radio-' + this.weight;
33938 cfg.cls += ' roo-radio-set-inline';
33942 ['xs','sm','md','lg'].map(function(size){
33943 if (settings[size]) {
33944 cfg.cls += ' col-' + size + '-' + settings[size];
33952 initEvents : function()
33954 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33955 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33957 if(!this.fieldLabel.length){
33958 this.labelEl.hide();
33961 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33962 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33964 this.indicator = this.indicatorEl();
33966 if(this.indicator){
33967 this.indicator.addClass('invisible');
33970 this.originalValue = this.getValue();
33974 inputEl: function ()
33976 return this.el.select('.roo-radio-set-input', true).first();
33979 getChildContainer : function()
33981 return this.itemsEl;
33984 register : function(item)
33986 this.radioes.push(item);
33990 validate : function()
33992 if(this.getVisibilityEl().hasClass('hidden')){
33998 Roo.each(this.radioes, function(i){
34007 if(this.allowBlank) {
34011 if(this.disabled || valid){
34016 this.markInvalid();
34021 markValid : function()
34023 if(this.labelEl.isVisible(true)){
34024 this.indicatorEl().removeClass('visible');
34025 this.indicatorEl().addClass('invisible');
34028 this.el.removeClass([this.invalidClass, this.validClass]);
34029 this.el.addClass(this.validClass);
34031 this.fireEvent('valid', this);
34034 markInvalid : function(msg)
34036 if(this.allowBlank || this.disabled){
34040 if(this.labelEl.isVisible(true)){
34041 this.indicatorEl().removeClass('invisible');
34042 this.indicatorEl().addClass('visible');
34045 this.el.removeClass([this.invalidClass, this.validClass]);
34046 this.el.addClass(this.invalidClass);
34048 this.fireEvent('invalid', this, msg);
34052 setValue : function(v, suppressEvent)
34054 if(this.value === v){
34061 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34064 Roo.each(this.radioes, function(i){
34066 i.el.removeClass('checked');
34069 Roo.each(this.radioes, function(i){
34071 if(i.value === v || i.value.toString() === v.toString()){
34073 i.el.addClass('checked');
34075 if(suppressEvent !== true){
34076 this.fireEvent('check', this, i);
34087 clearInvalid : function(){
34089 if(!this.el || this.preventMark){
34093 this.el.removeClass([this.invalidClass]);
34095 this.fireEvent('valid', this);
34100 Roo.apply(Roo.bootstrap.RadioSet, {
34104 register : function(set)
34106 this.groups[set.name] = set;
34109 get: function(name)
34111 if (typeof(this.groups[name]) == 'undefined') {
34115 return this.groups[name] ;
34121 * Ext JS Library 1.1.1
34122 * Copyright(c) 2006-2007, Ext JS, LLC.
34124 * Originally Released Under LGPL - original licence link has changed is not relivant.
34127 * <script type="text/javascript">
34132 * @class Roo.bootstrap.SplitBar
34133 * @extends Roo.util.Observable
34134 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34138 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34139 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34140 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34141 split.minSize = 100;
34142 split.maxSize = 600;
34143 split.animate = true;
34144 split.on('moved', splitterMoved);
34147 * Create a new SplitBar
34148 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34149 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34150 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34151 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34152 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34153 position of the SplitBar).
34155 Roo.bootstrap.SplitBar = function(cfg){
34160 // dragElement : elm
34161 // resizingElement: el,
34163 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34164 // placement : Roo.bootstrap.SplitBar.LEFT ,
34165 // existingProxy ???
34168 this.el = Roo.get(cfg.dragElement, true);
34169 this.el.dom.unselectable = "on";
34171 this.resizingEl = Roo.get(cfg.resizingElement, true);
34175 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34176 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34179 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34182 * The minimum size of the resizing element. (Defaults to 0)
34188 * The maximum size of the resizing element. (Defaults to 2000)
34191 this.maxSize = 2000;
34194 * Whether to animate the transition to the new size
34197 this.animate = false;
34200 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34203 this.useShim = false;
34208 if(!cfg.existingProxy){
34210 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34212 this.proxy = Roo.get(cfg.existingProxy).dom;
34215 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34218 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34221 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34224 this.dragSpecs = {};
34227 * @private The adapter to use to positon and resize elements
34229 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34230 this.adapter.init(this);
34232 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34234 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34235 this.el.addClass("roo-splitbar-h");
34238 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34239 this.el.addClass("roo-splitbar-v");
34245 * Fires when the splitter is moved (alias for {@link #event-moved})
34246 * @param {Roo.bootstrap.SplitBar} this
34247 * @param {Number} newSize the new width or height
34252 * Fires when the splitter is moved
34253 * @param {Roo.bootstrap.SplitBar} this
34254 * @param {Number} newSize the new width or height
34258 * @event beforeresize
34259 * Fires before the splitter is dragged
34260 * @param {Roo.bootstrap.SplitBar} this
34262 "beforeresize" : true,
34264 "beforeapply" : true
34267 Roo.util.Observable.call(this);
34270 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34271 onStartProxyDrag : function(x, y){
34272 this.fireEvent("beforeresize", this);
34274 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34276 o.enableDisplayMode("block");
34277 // all splitbars share the same overlay
34278 Roo.bootstrap.SplitBar.prototype.overlay = o;
34280 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34281 this.overlay.show();
34282 Roo.get(this.proxy).setDisplayed("block");
34283 var size = this.adapter.getElementSize(this);
34284 this.activeMinSize = this.getMinimumSize();;
34285 this.activeMaxSize = this.getMaximumSize();;
34286 var c1 = size - this.activeMinSize;
34287 var c2 = Math.max(this.activeMaxSize - size, 0);
34288 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34289 this.dd.resetConstraints();
34290 this.dd.setXConstraint(
34291 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34292 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34294 this.dd.setYConstraint(0, 0);
34296 this.dd.resetConstraints();
34297 this.dd.setXConstraint(0, 0);
34298 this.dd.setYConstraint(
34299 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34300 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34303 this.dragSpecs.startSize = size;
34304 this.dragSpecs.startPoint = [x, y];
34305 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34309 * @private Called after the drag operation by the DDProxy
34311 onEndProxyDrag : function(e){
34312 Roo.get(this.proxy).setDisplayed(false);
34313 var endPoint = Roo.lib.Event.getXY(e);
34315 this.overlay.hide();
34318 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34319 newSize = this.dragSpecs.startSize +
34320 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34321 endPoint[0] - this.dragSpecs.startPoint[0] :
34322 this.dragSpecs.startPoint[0] - endPoint[0]
34325 newSize = this.dragSpecs.startSize +
34326 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34327 endPoint[1] - this.dragSpecs.startPoint[1] :
34328 this.dragSpecs.startPoint[1] - endPoint[1]
34331 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34332 if(newSize != this.dragSpecs.startSize){
34333 if(this.fireEvent('beforeapply', this, newSize) !== false){
34334 this.adapter.setElementSize(this, newSize);
34335 this.fireEvent("moved", this, newSize);
34336 this.fireEvent("resize", this, newSize);
34342 * Get the adapter this SplitBar uses
34343 * @return The adapter object
34345 getAdapter : function(){
34346 return this.adapter;
34350 * Set the adapter this SplitBar uses
34351 * @param {Object} adapter A SplitBar adapter object
34353 setAdapter : function(adapter){
34354 this.adapter = adapter;
34355 this.adapter.init(this);
34359 * Gets the minimum size for the resizing element
34360 * @return {Number} The minimum size
34362 getMinimumSize : function(){
34363 return this.minSize;
34367 * Sets the minimum size for the resizing element
34368 * @param {Number} minSize The minimum size
34370 setMinimumSize : function(minSize){
34371 this.minSize = minSize;
34375 * Gets the maximum size for the resizing element
34376 * @return {Number} The maximum size
34378 getMaximumSize : function(){
34379 return this.maxSize;
34383 * Sets the maximum size for the resizing element
34384 * @param {Number} maxSize The maximum size
34386 setMaximumSize : function(maxSize){
34387 this.maxSize = maxSize;
34391 * Sets the initialize size for the resizing element
34392 * @param {Number} size The initial size
34394 setCurrentSize : function(size){
34395 var oldAnimate = this.animate;
34396 this.animate = false;
34397 this.adapter.setElementSize(this, size);
34398 this.animate = oldAnimate;
34402 * Destroy this splitbar.
34403 * @param {Boolean} removeEl True to remove the element
34405 destroy : function(removeEl){
34407 this.shim.remove();
34410 this.proxy.parentNode.removeChild(this.proxy);
34418 * @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.
34420 Roo.bootstrap.SplitBar.createProxy = function(dir){
34421 var proxy = new Roo.Element(document.createElement("div"));
34422 proxy.unselectable();
34423 var cls = 'roo-splitbar-proxy';
34424 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34425 document.body.appendChild(proxy.dom);
34430 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34431 * Default Adapter. It assumes the splitter and resizing element are not positioned
34432 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34434 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34437 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34438 // do nothing for now
34439 init : function(s){
34443 * Called before drag operations to get the current size of the resizing element.
34444 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34446 getElementSize : function(s){
34447 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34448 return s.resizingEl.getWidth();
34450 return s.resizingEl.getHeight();
34455 * Called after drag operations to set the size of the resizing element.
34456 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34457 * @param {Number} newSize The new size to set
34458 * @param {Function} onComplete A function to be invoked when resizing is complete
34460 setElementSize : function(s, newSize, onComplete){
34461 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34463 s.resizingEl.setWidth(newSize);
34465 onComplete(s, newSize);
34468 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34473 s.resizingEl.setHeight(newSize);
34475 onComplete(s, newSize);
34478 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34485 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34486 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34487 * Adapter that moves the splitter element to align with the resized sizing element.
34488 * Used with an absolute positioned SplitBar.
34489 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34490 * document.body, make sure you assign an id to the body element.
34492 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34493 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34494 this.container = Roo.get(container);
34497 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34498 init : function(s){
34499 this.basic.init(s);
34502 getElementSize : function(s){
34503 return this.basic.getElementSize(s);
34506 setElementSize : function(s, newSize, onComplete){
34507 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34510 moveSplitter : function(s){
34511 var yes = Roo.bootstrap.SplitBar;
34512 switch(s.placement){
34514 s.el.setX(s.resizingEl.getRight());
34517 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34520 s.el.setY(s.resizingEl.getBottom());
34523 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34530 * Orientation constant - Create a vertical SplitBar
34534 Roo.bootstrap.SplitBar.VERTICAL = 1;
34537 * Orientation constant - Create a horizontal SplitBar
34541 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34544 * Placement constant - The resizing element is to the left of the splitter element
34548 Roo.bootstrap.SplitBar.LEFT = 1;
34551 * Placement constant - The resizing element is to the right of the splitter element
34555 Roo.bootstrap.SplitBar.RIGHT = 2;
34558 * Placement constant - The resizing element is positioned above the splitter element
34562 Roo.bootstrap.SplitBar.TOP = 3;
34565 * Placement constant - The resizing element is positioned under splitter element
34569 Roo.bootstrap.SplitBar.BOTTOM = 4;
34570 Roo.namespace("Roo.bootstrap.layout");/*
34572 * Ext JS Library 1.1.1
34573 * Copyright(c) 2006-2007, Ext JS, LLC.
34575 * Originally Released Under LGPL - original licence link has changed is not relivant.
34578 * <script type="text/javascript">
34582 * @class Roo.bootstrap.layout.Manager
34583 * @extends Roo.bootstrap.Component
34584 * Base class for layout managers.
34586 Roo.bootstrap.layout.Manager = function(config)
34588 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34594 /** false to disable window resize monitoring @type Boolean */
34595 this.monitorWindowResize = true;
34600 * Fires when a layout is performed.
34601 * @param {Roo.LayoutManager} this
34605 * @event regionresized
34606 * Fires when the user resizes a region.
34607 * @param {Roo.LayoutRegion} region The resized region
34608 * @param {Number} newSize The new size (width for east/west, height for north/south)
34610 "regionresized" : true,
34612 * @event regioncollapsed
34613 * Fires when a region is collapsed.
34614 * @param {Roo.LayoutRegion} region The collapsed region
34616 "regioncollapsed" : true,
34618 * @event regionexpanded
34619 * Fires when a region is expanded.
34620 * @param {Roo.LayoutRegion} region The expanded region
34622 "regionexpanded" : true
34624 this.updating = false;
34627 this.el = Roo.get(config.el);
34633 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34638 monitorWindowResize : true,
34644 onRender : function(ct, position)
34647 this.el = Roo.get(ct);
34650 //this.fireEvent('render',this);
34654 initEvents: function()
34658 // ie scrollbar fix
34659 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34660 document.body.scroll = "no";
34661 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34662 this.el.position('relative');
34664 this.id = this.el.id;
34665 this.el.addClass("roo-layout-container");
34666 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34667 if(this.el.dom != document.body ) {
34668 this.el.on('resize', this.layout,this);
34669 this.el.on('show', this.layout,this);
34675 * Returns true if this layout is currently being updated
34676 * @return {Boolean}
34678 isUpdating : function(){
34679 return this.updating;
34683 * Suspend the LayoutManager from doing auto-layouts while
34684 * making multiple add or remove calls
34686 beginUpdate : function(){
34687 this.updating = true;
34691 * Restore auto-layouts and optionally disable the manager from performing a layout
34692 * @param {Boolean} noLayout true to disable a layout update
34694 endUpdate : function(noLayout){
34695 this.updating = false;
34701 layout: function(){
34705 onRegionResized : function(region, newSize){
34706 this.fireEvent("regionresized", region, newSize);
34710 onRegionCollapsed : function(region){
34711 this.fireEvent("regioncollapsed", region);
34714 onRegionExpanded : function(region){
34715 this.fireEvent("regionexpanded", region);
34719 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34720 * performs box-model adjustments.
34721 * @return {Object} The size as an object {width: (the width), height: (the height)}
34723 getViewSize : function()
34726 if(this.el.dom != document.body){
34727 size = this.el.getSize();
34729 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34731 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34732 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34737 * Returns the Element this layout is bound to.
34738 * @return {Roo.Element}
34740 getEl : function(){
34745 * Returns the specified region.
34746 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34747 * @return {Roo.LayoutRegion}
34749 getRegion : function(target){
34750 return this.regions[target.toLowerCase()];
34753 onWindowResize : function(){
34754 if(this.monitorWindowResize){
34761 * Ext JS Library 1.1.1
34762 * Copyright(c) 2006-2007, Ext JS, LLC.
34764 * Originally Released Under LGPL - original licence link has changed is not relivant.
34767 * <script type="text/javascript">
34770 * @class Roo.bootstrap.layout.Border
34771 * @extends Roo.bootstrap.layout.Manager
34772 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34773 * please see: examples/bootstrap/nested.html<br><br>
34775 <b>The container the layout is rendered into can be either the body element or any other element.
34776 If it is not the body element, the container needs to either be an absolute positioned element,
34777 or you will need to add "position:relative" to the css of the container. You will also need to specify
34778 the container size if it is not the body element.</b>
34781 * Create a new Border
34782 * @param {Object} config Configuration options
34784 Roo.bootstrap.layout.Border = function(config){
34785 config = config || {};
34786 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34790 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34791 if(config[region]){
34792 config[region].region = region;
34793 this.addRegion(config[region]);
34799 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34801 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34803 * Creates and adds a new region if it doesn't already exist.
34804 * @param {String} target The target region key (north, south, east, west or center).
34805 * @param {Object} config The regions config object
34806 * @return {BorderLayoutRegion} The new region
34808 addRegion : function(config)
34810 if(!this.regions[config.region]){
34811 var r = this.factory(config);
34812 this.bindRegion(r);
34814 return this.regions[config.region];
34818 bindRegion : function(r){
34819 this.regions[r.config.region] = r;
34821 r.on("visibilitychange", this.layout, this);
34822 r.on("paneladded", this.layout, this);
34823 r.on("panelremoved", this.layout, this);
34824 r.on("invalidated", this.layout, this);
34825 r.on("resized", this.onRegionResized, this);
34826 r.on("collapsed", this.onRegionCollapsed, this);
34827 r.on("expanded", this.onRegionExpanded, this);
34831 * Performs a layout update.
34833 layout : function()
34835 if(this.updating) {
34839 // render all the rebions if they have not been done alreayd?
34840 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34841 if(this.regions[region] && !this.regions[region].bodyEl){
34842 this.regions[region].onRender(this.el)
34846 var size = this.getViewSize();
34847 var w = size.width;
34848 var h = size.height;
34853 //var x = 0, y = 0;
34855 var rs = this.regions;
34856 var north = rs["north"];
34857 var south = rs["south"];
34858 var west = rs["west"];
34859 var east = rs["east"];
34860 var center = rs["center"];
34861 //if(this.hideOnLayout){ // not supported anymore
34862 //c.el.setStyle("display", "none");
34864 if(north && north.isVisible()){
34865 var b = north.getBox();
34866 var m = north.getMargins();
34867 b.width = w - (m.left+m.right);
34870 centerY = b.height + b.y + m.bottom;
34871 centerH -= centerY;
34872 north.updateBox(this.safeBox(b));
34874 if(south && south.isVisible()){
34875 var b = south.getBox();
34876 var m = south.getMargins();
34877 b.width = w - (m.left+m.right);
34879 var totalHeight = (b.height + m.top + m.bottom);
34880 b.y = h - totalHeight + m.top;
34881 centerH -= totalHeight;
34882 south.updateBox(this.safeBox(b));
34884 if(west && west.isVisible()){
34885 var b = west.getBox();
34886 var m = west.getMargins();
34887 b.height = centerH - (m.top+m.bottom);
34889 b.y = centerY + m.top;
34890 var totalWidth = (b.width + m.left + m.right);
34891 centerX += totalWidth;
34892 centerW -= totalWidth;
34893 west.updateBox(this.safeBox(b));
34895 if(east && east.isVisible()){
34896 var b = east.getBox();
34897 var m = east.getMargins();
34898 b.height = centerH - (m.top+m.bottom);
34899 var totalWidth = (b.width + m.left + m.right);
34900 b.x = w - totalWidth + m.left;
34901 b.y = centerY + m.top;
34902 centerW -= totalWidth;
34903 east.updateBox(this.safeBox(b));
34906 var m = center.getMargins();
34908 x: centerX + m.left,
34909 y: centerY + m.top,
34910 width: centerW - (m.left+m.right),
34911 height: centerH - (m.top+m.bottom)
34913 //if(this.hideOnLayout){
34914 //center.el.setStyle("display", "block");
34916 center.updateBox(this.safeBox(centerBox));
34919 this.fireEvent("layout", this);
34923 safeBox : function(box){
34924 box.width = Math.max(0, box.width);
34925 box.height = Math.max(0, box.height);
34930 * Adds a ContentPanel (or subclass) to this layout.
34931 * @param {String} target The target region key (north, south, east, west or center).
34932 * @param {Roo.ContentPanel} panel The panel to add
34933 * @return {Roo.ContentPanel} The added panel
34935 add : function(target, panel){
34937 target = target.toLowerCase();
34938 return this.regions[target].add(panel);
34942 * Remove a ContentPanel (or subclass) to this layout.
34943 * @param {String} target The target region key (north, south, east, west or center).
34944 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34945 * @return {Roo.ContentPanel} The removed panel
34947 remove : function(target, panel){
34948 target = target.toLowerCase();
34949 return this.regions[target].remove(panel);
34953 * Searches all regions for a panel with the specified id
34954 * @param {String} panelId
34955 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34957 findPanel : function(panelId){
34958 var rs = this.regions;
34959 for(var target in rs){
34960 if(typeof rs[target] != "function"){
34961 var p = rs[target].getPanel(panelId);
34971 * Searches all regions for a panel with the specified id and activates (shows) it.
34972 * @param {String/ContentPanel} panelId The panels id or the panel itself
34973 * @return {Roo.ContentPanel} The shown panel or null
34975 showPanel : function(panelId) {
34976 var rs = this.regions;
34977 for(var target in rs){
34978 var r = rs[target];
34979 if(typeof r != "function"){
34980 if(r.hasPanel(panelId)){
34981 return r.showPanel(panelId);
34989 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34990 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34993 restoreState : function(provider){
34995 provider = Roo.state.Manager;
34997 var sm = new Roo.LayoutStateManager();
34998 sm.init(this, provider);
35004 * Adds a xtype elements to the layout.
35008 xtype : 'ContentPanel',
35015 xtype : 'NestedLayoutPanel',
35021 items : [ ... list of content panels or nested layout panels.. ]
35025 * @param {Object} cfg Xtype definition of item to add.
35027 addxtype : function(cfg)
35029 // basically accepts a pannel...
35030 // can accept a layout region..!?!?
35031 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35034 // theory? children can only be panels??
35036 //if (!cfg.xtype.match(/Panel$/)) {
35041 if (typeof(cfg.region) == 'undefined') {
35042 Roo.log("Failed to add Panel, region was not set");
35046 var region = cfg.region;
35052 xitems = cfg.items;
35059 case 'Content': // ContentPanel (el, cfg)
35060 case 'Scroll': // ContentPanel (el, cfg)
35062 cfg.autoCreate = true;
35063 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35065 // var el = this.el.createChild();
35066 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35069 this.add(region, ret);
35073 case 'TreePanel': // our new panel!
35074 cfg.el = this.el.createChild();
35075 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35076 this.add(region, ret);
35081 // create a new Layout (which is a Border Layout...
35083 var clayout = cfg.layout;
35084 clayout.el = this.el.createChild();
35085 clayout.items = clayout.items || [];
35089 // replace this exitems with the clayout ones..
35090 xitems = clayout.items;
35092 // force background off if it's in center...
35093 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35094 cfg.background = false;
35096 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35099 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35100 //console.log('adding nested layout panel ' + cfg.toSource());
35101 this.add(region, ret);
35102 nb = {}; /// find first...
35107 // needs grid and region
35109 //var el = this.getRegion(region).el.createChild();
35111 *var el = this.el.createChild();
35112 // create the grid first...
35113 cfg.grid.container = el;
35114 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35117 if (region == 'center' && this.active ) {
35118 cfg.background = false;
35121 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35123 this.add(region, ret);
35125 if (cfg.background) {
35126 // render grid on panel activation (if panel background)
35127 ret.on('activate', function(gp) {
35128 if (!gp.grid.rendered) {
35129 // gp.grid.render(el);
35133 // cfg.grid.render(el);
35139 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35140 // it was the old xcomponent building that caused this before.
35141 // espeically if border is the top element in the tree.
35151 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35153 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35154 this.add(region, ret);
35158 throw "Can not add '" + cfg.xtype + "' to Border";
35164 this.beginUpdate();
35168 Roo.each(xitems, function(i) {
35169 region = nb && i.region ? i.region : false;
35171 var add = ret.addxtype(i);
35174 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35175 if (!i.background) {
35176 abn[region] = nb[region] ;
35183 // make the last non-background panel active..
35184 //if (nb) { Roo.log(abn); }
35187 for(var r in abn) {
35188 region = this.getRegion(r);
35190 // tried using nb[r], but it does not work..
35192 region.showPanel(abn[r]);
35203 factory : function(cfg)
35206 var validRegions = Roo.bootstrap.layout.Border.regions;
35208 var target = cfg.region;
35211 var r = Roo.bootstrap.layout;
35215 return new r.North(cfg);
35217 return new r.South(cfg);
35219 return new r.East(cfg);
35221 return new r.West(cfg);
35223 return new r.Center(cfg);
35225 throw 'Layout region "'+target+'" not supported.';
35232 * Ext JS Library 1.1.1
35233 * Copyright(c) 2006-2007, Ext JS, LLC.
35235 * Originally Released Under LGPL - original licence link has changed is not relivant.
35238 * <script type="text/javascript">
35242 * @class Roo.bootstrap.layout.Basic
35243 * @extends Roo.util.Observable
35244 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35245 * and does not have a titlebar, tabs or any other features. All it does is size and position
35246 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35247 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35248 * @cfg {string} region the region that it inhabits..
35249 * @cfg {bool} skipConfig skip config?
35253 Roo.bootstrap.layout.Basic = function(config){
35255 this.mgr = config.mgr;
35257 this.position = config.region;
35259 var skipConfig = config.skipConfig;
35263 * @scope Roo.BasicLayoutRegion
35267 * @event beforeremove
35268 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35269 * @param {Roo.LayoutRegion} this
35270 * @param {Roo.ContentPanel} panel The panel
35271 * @param {Object} e The cancel event object
35273 "beforeremove" : true,
35275 * @event invalidated
35276 * Fires when the layout for this region is changed.
35277 * @param {Roo.LayoutRegion} this
35279 "invalidated" : true,
35281 * @event visibilitychange
35282 * Fires when this region is shown or hidden
35283 * @param {Roo.LayoutRegion} this
35284 * @param {Boolean} visibility true or false
35286 "visibilitychange" : true,
35288 * @event paneladded
35289 * Fires when a panel is added.
35290 * @param {Roo.LayoutRegion} this
35291 * @param {Roo.ContentPanel} panel The panel
35293 "paneladded" : true,
35295 * @event panelremoved
35296 * Fires when a panel is removed.
35297 * @param {Roo.LayoutRegion} this
35298 * @param {Roo.ContentPanel} panel The panel
35300 "panelremoved" : true,
35302 * @event beforecollapse
35303 * Fires when this region before collapse.
35304 * @param {Roo.LayoutRegion} this
35306 "beforecollapse" : true,
35309 * Fires when this region is collapsed.
35310 * @param {Roo.LayoutRegion} this
35312 "collapsed" : true,
35315 * Fires when this region is expanded.
35316 * @param {Roo.LayoutRegion} this
35321 * Fires when this region is slid into view.
35322 * @param {Roo.LayoutRegion} this
35324 "slideshow" : true,
35327 * Fires when this region slides out of view.
35328 * @param {Roo.LayoutRegion} this
35330 "slidehide" : true,
35332 * @event panelactivated
35333 * Fires when a panel is activated.
35334 * @param {Roo.LayoutRegion} this
35335 * @param {Roo.ContentPanel} panel The activated panel
35337 "panelactivated" : true,
35340 * Fires when the user resizes this region.
35341 * @param {Roo.LayoutRegion} this
35342 * @param {Number} newSize The new size (width for east/west, height for north/south)
35346 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35347 this.panels = new Roo.util.MixedCollection();
35348 this.panels.getKey = this.getPanelId.createDelegate(this);
35350 this.activePanel = null;
35351 // ensure listeners are added...
35353 if (config.listeners || config.events) {
35354 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35355 listeners : config.listeners || {},
35356 events : config.events || {}
35360 if(skipConfig !== true){
35361 this.applyConfig(config);
35365 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35367 getPanelId : function(p){
35371 applyConfig : function(config){
35372 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35373 this.config = config;
35378 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35379 * the width, for horizontal (north, south) the height.
35380 * @param {Number} newSize The new width or height
35382 resizeTo : function(newSize){
35383 var el = this.el ? this.el :
35384 (this.activePanel ? this.activePanel.getEl() : null);
35386 switch(this.position){
35389 el.setWidth(newSize);
35390 this.fireEvent("resized", this, newSize);
35394 el.setHeight(newSize);
35395 this.fireEvent("resized", this, newSize);
35401 getBox : function(){
35402 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35405 getMargins : function(){
35406 return this.margins;
35409 updateBox : function(box){
35411 var el = this.activePanel.getEl();
35412 el.dom.style.left = box.x + "px";
35413 el.dom.style.top = box.y + "px";
35414 this.activePanel.setSize(box.width, box.height);
35418 * Returns the container element for this region.
35419 * @return {Roo.Element}
35421 getEl : function(){
35422 return this.activePanel;
35426 * Returns true if this region is currently visible.
35427 * @return {Boolean}
35429 isVisible : function(){
35430 return this.activePanel ? true : false;
35433 setActivePanel : function(panel){
35434 panel = this.getPanel(panel);
35435 if(this.activePanel && this.activePanel != panel){
35436 this.activePanel.setActiveState(false);
35437 this.activePanel.getEl().setLeftTop(-10000,-10000);
35439 this.activePanel = panel;
35440 panel.setActiveState(true);
35442 panel.setSize(this.box.width, this.box.height);
35444 this.fireEvent("panelactivated", this, panel);
35445 this.fireEvent("invalidated");
35449 * Show the specified panel.
35450 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35451 * @return {Roo.ContentPanel} The shown panel or null
35453 showPanel : function(panel){
35454 panel = this.getPanel(panel);
35456 this.setActivePanel(panel);
35462 * Get the active panel for this region.
35463 * @return {Roo.ContentPanel} The active panel or null
35465 getActivePanel : function(){
35466 return this.activePanel;
35470 * Add the passed ContentPanel(s)
35471 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35472 * @return {Roo.ContentPanel} The panel added (if only one was added)
35474 add : function(panel){
35475 if(arguments.length > 1){
35476 for(var i = 0, len = arguments.length; i < len; i++) {
35477 this.add(arguments[i]);
35481 if(this.hasPanel(panel)){
35482 this.showPanel(panel);
35485 var el = panel.getEl();
35486 if(el.dom.parentNode != this.mgr.el.dom){
35487 this.mgr.el.dom.appendChild(el.dom);
35489 if(panel.setRegion){
35490 panel.setRegion(this);
35492 this.panels.add(panel);
35493 el.setStyle("position", "absolute");
35494 if(!panel.background){
35495 this.setActivePanel(panel);
35496 if(this.config.initialSize && this.panels.getCount()==1){
35497 this.resizeTo(this.config.initialSize);
35500 this.fireEvent("paneladded", this, panel);
35505 * Returns true if the panel is in this region.
35506 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35507 * @return {Boolean}
35509 hasPanel : function(panel){
35510 if(typeof panel == "object"){ // must be panel obj
35511 panel = panel.getId();
35513 return this.getPanel(panel) ? true : false;
35517 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35518 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35519 * @param {Boolean} preservePanel Overrides the config preservePanel option
35520 * @return {Roo.ContentPanel} The panel that was removed
35522 remove : function(panel, preservePanel){
35523 panel = this.getPanel(panel);
35528 this.fireEvent("beforeremove", this, panel, e);
35529 if(e.cancel === true){
35532 var panelId = panel.getId();
35533 this.panels.removeKey(panelId);
35538 * Returns the panel specified or null if it's not in this region.
35539 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35540 * @return {Roo.ContentPanel}
35542 getPanel : function(id){
35543 if(typeof id == "object"){ // must be panel obj
35546 return this.panels.get(id);
35550 * Returns this regions position (north/south/east/west/center).
35553 getPosition: function(){
35554 return this.position;
35558 * Ext JS Library 1.1.1
35559 * Copyright(c) 2006-2007, Ext JS, LLC.
35561 * Originally Released Under LGPL - original licence link has changed is not relivant.
35564 * <script type="text/javascript">
35568 * @class Roo.bootstrap.layout.Region
35569 * @extends Roo.bootstrap.layout.Basic
35570 * This class represents a region in a layout manager.
35572 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35573 * @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})
35574 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35575 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35576 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35577 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35578 * @cfg {String} title The title for the region (overrides panel titles)
35579 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35580 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35581 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35582 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35583 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35584 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35585 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35586 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35587 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35588 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35590 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35591 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35592 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35593 * @cfg {Number} width For East/West panels
35594 * @cfg {Number} height For North/South panels
35595 * @cfg {Boolean} split To show the splitter
35596 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35598 * @cfg {string} cls Extra CSS classes to add to region
35600 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35601 * @cfg {string} region the region that it inhabits..
35604 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35605 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35607 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35608 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35609 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35611 Roo.bootstrap.layout.Region = function(config)
35613 this.applyConfig(config);
35615 var mgr = config.mgr;
35616 var pos = config.region;
35617 config.skipConfig = true;
35618 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35621 this.onRender(mgr.el);
35624 this.visible = true;
35625 this.collapsed = false;
35626 this.unrendered_panels = [];
35629 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35631 position: '', // set by wrapper (eg. north/south etc..)
35632 unrendered_panels : null, // unrendered panels.
35633 createBody : function(){
35634 /** This region's body element
35635 * @type Roo.Element */
35636 this.bodyEl = this.el.createChild({
35638 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35642 onRender: function(ctr, pos)
35644 var dh = Roo.DomHelper;
35645 /** This region's container element
35646 * @type Roo.Element */
35647 this.el = dh.append(ctr.dom, {
35649 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35651 /** This region's title element
35652 * @type Roo.Element */
35654 this.titleEl = dh.append(this.el.dom,
35657 unselectable: "on",
35658 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35660 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35661 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35664 this.titleEl.enableDisplayMode();
35665 /** This region's title text element
35666 * @type HTMLElement */
35667 this.titleTextEl = this.titleEl.dom.firstChild;
35668 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35670 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35671 this.closeBtn.enableDisplayMode();
35672 this.closeBtn.on("click", this.closeClicked, this);
35673 this.closeBtn.hide();
35675 this.createBody(this.config);
35676 if(this.config.hideWhenEmpty){
35678 this.on("paneladded", this.validateVisibility, this);
35679 this.on("panelremoved", this.validateVisibility, this);
35681 if(this.autoScroll){
35682 this.bodyEl.setStyle("overflow", "auto");
35684 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35686 //if(c.titlebar !== false){
35687 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35688 this.titleEl.hide();
35690 this.titleEl.show();
35691 if(this.config.title){
35692 this.titleTextEl.innerHTML = this.config.title;
35696 if(this.config.collapsed){
35697 this.collapse(true);
35699 if(this.config.hidden){
35703 if (this.unrendered_panels && this.unrendered_panels.length) {
35704 for (var i =0;i< this.unrendered_panels.length; i++) {
35705 this.add(this.unrendered_panels[i]);
35707 this.unrendered_panels = null;
35713 applyConfig : function(c)
35716 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35717 var dh = Roo.DomHelper;
35718 if(c.titlebar !== false){
35719 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35720 this.collapseBtn.on("click", this.collapse, this);
35721 this.collapseBtn.enableDisplayMode();
35723 if(c.showPin === true || this.showPin){
35724 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35725 this.stickBtn.enableDisplayMode();
35726 this.stickBtn.on("click", this.expand, this);
35727 this.stickBtn.hide();
35732 /** This region's collapsed element
35733 * @type Roo.Element */
35736 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35737 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35740 if(c.floatable !== false){
35741 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35742 this.collapsedEl.on("click", this.collapseClick, this);
35745 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35746 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35747 id: "message", unselectable: "on", style:{"float":"left"}});
35748 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35750 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35751 this.expandBtn.on("click", this.expand, this);
35755 if(this.collapseBtn){
35756 this.collapseBtn.setVisible(c.collapsible == true);
35759 this.cmargins = c.cmargins || this.cmargins ||
35760 (this.position == "west" || this.position == "east" ?
35761 {top: 0, left: 2, right:2, bottom: 0} :
35762 {top: 2, left: 0, right:0, bottom: 2});
35764 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35767 this.bottomTabs = c.tabPosition != "top";
35769 this.autoScroll = c.autoScroll || false;
35774 this.duration = c.duration || .30;
35775 this.slideDuration = c.slideDuration || .45;
35780 * Returns true if this region is currently visible.
35781 * @return {Boolean}
35783 isVisible : function(){
35784 return this.visible;
35788 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35789 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35791 //setCollapsedTitle : function(title){
35792 // title = title || " ";
35793 // if(this.collapsedTitleTextEl){
35794 // this.collapsedTitleTextEl.innerHTML = title;
35798 getBox : function(){
35800 // if(!this.collapsed){
35801 b = this.el.getBox(false, true);
35803 // b = this.collapsedEl.getBox(false, true);
35808 getMargins : function(){
35809 return this.margins;
35810 //return this.collapsed ? this.cmargins : this.margins;
35813 highlight : function(){
35814 this.el.addClass("x-layout-panel-dragover");
35817 unhighlight : function(){
35818 this.el.removeClass("x-layout-panel-dragover");
35821 updateBox : function(box)
35823 if (!this.bodyEl) {
35824 return; // not rendered yet..
35828 if(!this.collapsed){
35829 this.el.dom.style.left = box.x + "px";
35830 this.el.dom.style.top = box.y + "px";
35831 this.updateBody(box.width, box.height);
35833 this.collapsedEl.dom.style.left = box.x + "px";
35834 this.collapsedEl.dom.style.top = box.y + "px";
35835 this.collapsedEl.setSize(box.width, box.height);
35838 this.tabs.autoSizeTabs();
35842 updateBody : function(w, h)
35845 this.el.setWidth(w);
35846 w -= this.el.getBorderWidth("rl");
35847 if(this.config.adjustments){
35848 w += this.config.adjustments[0];
35851 if(h !== null && h > 0){
35852 this.el.setHeight(h);
35853 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35854 h -= this.el.getBorderWidth("tb");
35855 if(this.config.adjustments){
35856 h += this.config.adjustments[1];
35858 this.bodyEl.setHeight(h);
35860 h = this.tabs.syncHeight(h);
35863 if(this.panelSize){
35864 w = w !== null ? w : this.panelSize.width;
35865 h = h !== null ? h : this.panelSize.height;
35867 if(this.activePanel){
35868 var el = this.activePanel.getEl();
35869 w = w !== null ? w : el.getWidth();
35870 h = h !== null ? h : el.getHeight();
35871 this.panelSize = {width: w, height: h};
35872 this.activePanel.setSize(w, h);
35874 if(Roo.isIE && this.tabs){
35875 this.tabs.el.repaint();
35880 * Returns the container element for this region.
35881 * @return {Roo.Element}
35883 getEl : function(){
35888 * Hides this region.
35891 //if(!this.collapsed){
35892 this.el.dom.style.left = "-2000px";
35895 // this.collapsedEl.dom.style.left = "-2000px";
35896 // this.collapsedEl.hide();
35898 this.visible = false;
35899 this.fireEvent("visibilitychange", this, false);
35903 * Shows this region if it was previously hidden.
35906 //if(!this.collapsed){
35909 // this.collapsedEl.show();
35911 this.visible = true;
35912 this.fireEvent("visibilitychange", this, true);
35915 closeClicked : function(){
35916 if(this.activePanel){
35917 this.remove(this.activePanel);
35921 collapseClick : function(e){
35923 e.stopPropagation();
35926 e.stopPropagation();
35932 * Collapses this region.
35933 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35936 collapse : function(skipAnim, skipCheck = false){
35937 if(this.collapsed) {
35941 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35943 this.collapsed = true;
35945 this.split.el.hide();
35947 if(this.config.animate && skipAnim !== true){
35948 this.fireEvent("invalidated", this);
35949 this.animateCollapse();
35951 this.el.setLocation(-20000,-20000);
35953 this.collapsedEl.show();
35954 this.fireEvent("collapsed", this);
35955 this.fireEvent("invalidated", this);
35961 animateCollapse : function(){
35966 * Expands this region if it was previously collapsed.
35967 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35968 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35971 expand : function(e, skipAnim){
35973 e.stopPropagation();
35975 if(!this.collapsed || this.el.hasActiveFx()) {
35979 this.afterSlideIn();
35982 this.collapsed = false;
35983 if(this.config.animate && skipAnim !== true){
35984 this.animateExpand();
35988 this.split.el.show();
35990 this.collapsedEl.setLocation(-2000,-2000);
35991 this.collapsedEl.hide();
35992 this.fireEvent("invalidated", this);
35993 this.fireEvent("expanded", this);
35997 animateExpand : function(){
36001 initTabs : function()
36003 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36005 var ts = new Roo.bootstrap.panel.Tabs({
36006 el: this.bodyEl.dom,
36007 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36008 disableTooltips: this.config.disableTabTips,
36009 toolbar : this.config.toolbar
36012 if(this.config.hideTabs){
36013 ts.stripWrap.setDisplayed(false);
36016 ts.resizeTabs = this.config.resizeTabs === true;
36017 ts.minTabWidth = this.config.minTabWidth || 40;
36018 ts.maxTabWidth = this.config.maxTabWidth || 250;
36019 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36020 ts.monitorResize = false;
36021 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36022 ts.bodyEl.addClass('roo-layout-tabs-body');
36023 this.panels.each(this.initPanelAsTab, this);
36026 initPanelAsTab : function(panel){
36027 var ti = this.tabs.addTab(
36031 this.config.closeOnTab && panel.isClosable(),
36034 if(panel.tabTip !== undefined){
36035 ti.setTooltip(panel.tabTip);
36037 ti.on("activate", function(){
36038 this.setActivePanel(panel);
36041 if(this.config.closeOnTab){
36042 ti.on("beforeclose", function(t, e){
36044 this.remove(panel);
36048 panel.tabItem = ti;
36053 updatePanelTitle : function(panel, title)
36055 if(this.activePanel == panel){
36056 this.updateTitle(title);
36059 var ti = this.tabs.getTab(panel.getEl().id);
36061 if(panel.tabTip !== undefined){
36062 ti.setTooltip(panel.tabTip);
36067 updateTitle : function(title){
36068 if(this.titleTextEl && !this.config.title){
36069 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36073 setActivePanel : function(panel)
36075 panel = this.getPanel(panel);
36076 if(this.activePanel && this.activePanel != panel){
36077 if(this.activePanel.setActiveState(false) === false){
36081 this.activePanel = panel;
36082 panel.setActiveState(true);
36083 if(this.panelSize){
36084 panel.setSize(this.panelSize.width, this.panelSize.height);
36087 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36089 this.updateTitle(panel.getTitle());
36091 this.fireEvent("invalidated", this);
36093 this.fireEvent("panelactivated", this, panel);
36097 * Shows the specified panel.
36098 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36099 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36101 showPanel : function(panel)
36103 panel = this.getPanel(panel);
36106 var tab = this.tabs.getTab(panel.getEl().id);
36107 if(tab.isHidden()){
36108 this.tabs.unhideTab(tab.id);
36112 this.setActivePanel(panel);
36119 * Get the active panel for this region.
36120 * @return {Roo.ContentPanel} The active panel or null
36122 getActivePanel : function(){
36123 return this.activePanel;
36126 validateVisibility : function(){
36127 if(this.panels.getCount() < 1){
36128 this.updateTitle(" ");
36129 this.closeBtn.hide();
36132 if(!this.isVisible()){
36139 * Adds the passed ContentPanel(s) to this region.
36140 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36141 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36143 add : function(panel)
36145 if(arguments.length > 1){
36146 for(var i = 0, len = arguments.length; i < len; i++) {
36147 this.add(arguments[i]);
36152 // if we have not been rendered yet, then we can not really do much of this..
36153 if (!this.bodyEl) {
36154 this.unrendered_panels.push(panel);
36161 if(this.hasPanel(panel)){
36162 this.showPanel(panel);
36165 panel.setRegion(this);
36166 this.panels.add(panel);
36167 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36168 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36169 // and hide them... ???
36170 this.bodyEl.dom.appendChild(panel.getEl().dom);
36171 if(panel.background !== true){
36172 this.setActivePanel(panel);
36174 this.fireEvent("paneladded", this, panel);
36181 this.initPanelAsTab(panel);
36185 if(panel.background !== true){
36186 this.tabs.activate(panel.getEl().id);
36188 this.fireEvent("paneladded", this, panel);
36193 * Hides the tab for the specified panel.
36194 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36196 hidePanel : function(panel){
36197 if(this.tabs && (panel = this.getPanel(panel))){
36198 this.tabs.hideTab(panel.getEl().id);
36203 * Unhides the tab for a previously hidden panel.
36204 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36206 unhidePanel : function(panel){
36207 if(this.tabs && (panel = this.getPanel(panel))){
36208 this.tabs.unhideTab(panel.getEl().id);
36212 clearPanels : function(){
36213 while(this.panels.getCount() > 0){
36214 this.remove(this.panels.first());
36219 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36220 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36221 * @param {Boolean} preservePanel Overrides the config preservePanel option
36222 * @return {Roo.ContentPanel} The panel that was removed
36224 remove : function(panel, preservePanel)
36226 panel = this.getPanel(panel);
36231 this.fireEvent("beforeremove", this, panel, e);
36232 if(e.cancel === true){
36235 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36236 var panelId = panel.getId();
36237 this.panels.removeKey(panelId);
36239 document.body.appendChild(panel.getEl().dom);
36242 this.tabs.removeTab(panel.getEl().id);
36243 }else if (!preservePanel){
36244 this.bodyEl.dom.removeChild(panel.getEl().dom);
36246 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36247 var p = this.panels.first();
36248 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36249 tempEl.appendChild(p.getEl().dom);
36250 this.bodyEl.update("");
36251 this.bodyEl.dom.appendChild(p.getEl().dom);
36253 this.updateTitle(p.getTitle());
36255 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36256 this.setActivePanel(p);
36258 panel.setRegion(null);
36259 if(this.activePanel == panel){
36260 this.activePanel = null;
36262 if(this.config.autoDestroy !== false && preservePanel !== true){
36263 try{panel.destroy();}catch(e){}
36265 this.fireEvent("panelremoved", this, panel);
36270 * Returns the TabPanel component used by this region
36271 * @return {Roo.TabPanel}
36273 getTabs : function(){
36277 createTool : function(parentEl, className){
36278 var btn = Roo.DomHelper.append(parentEl, {
36280 cls: "x-layout-tools-button",
36283 cls: "roo-layout-tools-button-inner " + className,
36287 btn.addClassOnOver("roo-layout-tools-button-over");
36292 * Ext JS Library 1.1.1
36293 * Copyright(c) 2006-2007, Ext JS, LLC.
36295 * Originally Released Under LGPL - original licence link has changed is not relivant.
36298 * <script type="text/javascript">
36304 * @class Roo.SplitLayoutRegion
36305 * @extends Roo.LayoutRegion
36306 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36308 Roo.bootstrap.layout.Split = function(config){
36309 this.cursor = config.cursor;
36310 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36313 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36315 splitTip : "Drag to resize.",
36316 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36317 useSplitTips : false,
36319 applyConfig : function(config){
36320 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36323 onRender : function(ctr,pos) {
36325 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36326 if(!this.config.split){
36331 var splitEl = Roo.DomHelper.append(ctr.dom, {
36333 id: this.el.id + "-split",
36334 cls: "roo-layout-split roo-layout-split-"+this.position,
36337 /** The SplitBar for this region
36338 * @type Roo.SplitBar */
36339 // does not exist yet...
36340 Roo.log([this.position, this.orientation]);
36342 this.split = new Roo.bootstrap.SplitBar({
36343 dragElement : splitEl,
36344 resizingElement: this.el,
36345 orientation : this.orientation
36348 this.split.on("moved", this.onSplitMove, this);
36349 this.split.useShim = this.config.useShim === true;
36350 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36351 if(this.useSplitTips){
36352 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36354 //if(config.collapsible){
36355 // this.split.el.on("dblclick", this.collapse, this);
36358 if(typeof this.config.minSize != "undefined"){
36359 this.split.minSize = this.config.minSize;
36361 if(typeof this.config.maxSize != "undefined"){
36362 this.split.maxSize = this.config.maxSize;
36364 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36365 this.hideSplitter();
36370 getHMaxSize : function(){
36371 var cmax = this.config.maxSize || 10000;
36372 var center = this.mgr.getRegion("center");
36373 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36376 getVMaxSize : function(){
36377 var cmax = this.config.maxSize || 10000;
36378 var center = this.mgr.getRegion("center");
36379 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36382 onSplitMove : function(split, newSize){
36383 this.fireEvent("resized", this, newSize);
36387 * Returns the {@link Roo.SplitBar} for this region.
36388 * @return {Roo.SplitBar}
36390 getSplitBar : function(){
36395 this.hideSplitter();
36396 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36399 hideSplitter : function(){
36401 this.split.el.setLocation(-2000,-2000);
36402 this.split.el.hide();
36408 this.split.el.show();
36410 Roo.bootstrap.layout.Split.superclass.show.call(this);
36413 beforeSlide: function(){
36414 if(Roo.isGecko){// firefox overflow auto bug workaround
36415 this.bodyEl.clip();
36417 this.tabs.bodyEl.clip();
36419 if(this.activePanel){
36420 this.activePanel.getEl().clip();
36422 if(this.activePanel.beforeSlide){
36423 this.activePanel.beforeSlide();
36429 afterSlide : function(){
36430 if(Roo.isGecko){// firefox overflow auto bug workaround
36431 this.bodyEl.unclip();
36433 this.tabs.bodyEl.unclip();
36435 if(this.activePanel){
36436 this.activePanel.getEl().unclip();
36437 if(this.activePanel.afterSlide){
36438 this.activePanel.afterSlide();
36444 initAutoHide : function(){
36445 if(this.autoHide !== false){
36446 if(!this.autoHideHd){
36447 var st = new Roo.util.DelayedTask(this.slideIn, this);
36448 this.autoHideHd = {
36449 "mouseout": function(e){
36450 if(!e.within(this.el, true)){
36454 "mouseover" : function(e){
36460 this.el.on(this.autoHideHd);
36464 clearAutoHide : function(){
36465 if(this.autoHide !== false){
36466 this.el.un("mouseout", this.autoHideHd.mouseout);
36467 this.el.un("mouseover", this.autoHideHd.mouseover);
36471 clearMonitor : function(){
36472 Roo.get(document).un("click", this.slideInIf, this);
36475 // these names are backwards but not changed for compat
36476 slideOut : function(){
36477 if(this.isSlid || this.el.hasActiveFx()){
36480 this.isSlid = true;
36481 if(this.collapseBtn){
36482 this.collapseBtn.hide();
36484 this.closeBtnState = this.closeBtn.getStyle('display');
36485 this.closeBtn.hide();
36487 this.stickBtn.show();
36490 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36491 this.beforeSlide();
36492 this.el.setStyle("z-index", 10001);
36493 this.el.slideIn(this.getSlideAnchor(), {
36494 callback: function(){
36496 this.initAutoHide();
36497 Roo.get(document).on("click", this.slideInIf, this);
36498 this.fireEvent("slideshow", this);
36505 afterSlideIn : function(){
36506 this.clearAutoHide();
36507 this.isSlid = false;
36508 this.clearMonitor();
36509 this.el.setStyle("z-index", "");
36510 if(this.collapseBtn){
36511 this.collapseBtn.show();
36513 this.closeBtn.setStyle('display', this.closeBtnState);
36515 this.stickBtn.hide();
36517 this.fireEvent("slidehide", this);
36520 slideIn : function(cb){
36521 if(!this.isSlid || this.el.hasActiveFx()){
36525 this.isSlid = false;
36526 this.beforeSlide();
36527 this.el.slideOut(this.getSlideAnchor(), {
36528 callback: function(){
36529 this.el.setLeftTop(-10000, -10000);
36531 this.afterSlideIn();
36539 slideInIf : function(e){
36540 if(!e.within(this.el)){
36545 animateCollapse : function(){
36546 this.beforeSlide();
36547 this.el.setStyle("z-index", 20000);
36548 var anchor = this.getSlideAnchor();
36549 this.el.slideOut(anchor, {
36550 callback : function(){
36551 this.el.setStyle("z-index", "");
36552 this.collapsedEl.slideIn(anchor, {duration:.3});
36554 this.el.setLocation(-10000,-10000);
36556 this.fireEvent("collapsed", this);
36563 animateExpand : function(){
36564 this.beforeSlide();
36565 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36566 this.el.setStyle("z-index", 20000);
36567 this.collapsedEl.hide({
36570 this.el.slideIn(this.getSlideAnchor(), {
36571 callback : function(){
36572 this.el.setStyle("z-index", "");
36575 this.split.el.show();
36577 this.fireEvent("invalidated", this);
36578 this.fireEvent("expanded", this);
36606 getAnchor : function(){
36607 return this.anchors[this.position];
36610 getCollapseAnchor : function(){
36611 return this.canchors[this.position];
36614 getSlideAnchor : function(){
36615 return this.sanchors[this.position];
36618 getAlignAdj : function(){
36619 var cm = this.cmargins;
36620 switch(this.position){
36636 getExpandAdj : function(){
36637 var c = this.collapsedEl, cm = this.cmargins;
36638 switch(this.position){
36640 return [-(cm.right+c.getWidth()+cm.left), 0];
36643 return [cm.right+c.getWidth()+cm.left, 0];
36646 return [0, -(cm.top+cm.bottom+c.getHeight())];
36649 return [0, cm.top+cm.bottom+c.getHeight()];
36655 * Ext JS Library 1.1.1
36656 * Copyright(c) 2006-2007, Ext JS, LLC.
36658 * Originally Released Under LGPL - original licence link has changed is not relivant.
36661 * <script type="text/javascript">
36664 * These classes are private internal classes
36666 Roo.bootstrap.layout.Center = function(config){
36667 config.region = "center";
36668 Roo.bootstrap.layout.Region.call(this, config);
36669 this.visible = true;
36670 this.minWidth = config.minWidth || 20;
36671 this.minHeight = config.minHeight || 20;
36674 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36676 // center panel can't be hidden
36680 // center panel can't be hidden
36683 getMinWidth: function(){
36684 return this.minWidth;
36687 getMinHeight: function(){
36688 return this.minHeight;
36701 Roo.bootstrap.layout.North = function(config)
36703 config.region = 'north';
36704 config.cursor = 'n-resize';
36706 Roo.bootstrap.layout.Split.call(this, config);
36710 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36711 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36712 this.split.el.addClass("roo-layout-split-v");
36714 var size = config.initialSize || config.height;
36715 if(typeof size != "undefined"){
36716 this.el.setHeight(size);
36719 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36721 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36725 getBox : function(){
36726 if(this.collapsed){
36727 return this.collapsedEl.getBox();
36729 var box = this.el.getBox();
36731 box.height += this.split.el.getHeight();
36736 updateBox : function(box){
36737 if(this.split && !this.collapsed){
36738 box.height -= this.split.el.getHeight();
36739 this.split.el.setLeft(box.x);
36740 this.split.el.setTop(box.y+box.height);
36741 this.split.el.setWidth(box.width);
36743 if(this.collapsed){
36744 this.updateBody(box.width, null);
36746 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36754 Roo.bootstrap.layout.South = function(config){
36755 config.region = 'south';
36756 config.cursor = 's-resize';
36757 Roo.bootstrap.layout.Split.call(this, config);
36759 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36760 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36761 this.split.el.addClass("roo-layout-split-v");
36763 var size = config.initialSize || config.height;
36764 if(typeof size != "undefined"){
36765 this.el.setHeight(size);
36769 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36770 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36771 getBox : function(){
36772 if(this.collapsed){
36773 return this.collapsedEl.getBox();
36775 var box = this.el.getBox();
36777 var sh = this.split.el.getHeight();
36784 updateBox : function(box){
36785 if(this.split && !this.collapsed){
36786 var sh = this.split.el.getHeight();
36789 this.split.el.setLeft(box.x);
36790 this.split.el.setTop(box.y-sh);
36791 this.split.el.setWidth(box.width);
36793 if(this.collapsed){
36794 this.updateBody(box.width, null);
36796 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36800 Roo.bootstrap.layout.East = function(config){
36801 config.region = "east";
36802 config.cursor = "e-resize";
36803 Roo.bootstrap.layout.Split.call(this, config);
36805 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36806 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36807 this.split.el.addClass("roo-layout-split-h");
36809 var size = config.initialSize || config.width;
36810 if(typeof size != "undefined"){
36811 this.el.setWidth(size);
36814 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36815 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36816 getBox : function(){
36817 if(this.collapsed){
36818 return this.collapsedEl.getBox();
36820 var box = this.el.getBox();
36822 var sw = this.split.el.getWidth();
36829 updateBox : function(box){
36830 if(this.split && !this.collapsed){
36831 var sw = this.split.el.getWidth();
36833 this.split.el.setLeft(box.x);
36834 this.split.el.setTop(box.y);
36835 this.split.el.setHeight(box.height);
36838 if(this.collapsed){
36839 this.updateBody(null, box.height);
36841 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36845 Roo.bootstrap.layout.West = function(config){
36846 config.region = "west";
36847 config.cursor = "w-resize";
36849 Roo.bootstrap.layout.Split.call(this, config);
36851 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36852 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36853 this.split.el.addClass("roo-layout-split-h");
36857 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36858 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36860 onRender: function(ctr, pos)
36862 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36863 var size = this.config.initialSize || this.config.width;
36864 if(typeof size != "undefined"){
36865 this.el.setWidth(size);
36869 getBox : function(){
36870 if(this.collapsed){
36871 return this.collapsedEl.getBox();
36873 var box = this.el.getBox();
36875 box.width += this.split.el.getWidth();
36880 updateBox : function(box){
36881 if(this.split && !this.collapsed){
36882 var sw = this.split.el.getWidth();
36884 this.split.el.setLeft(box.x+box.width);
36885 this.split.el.setTop(box.y);
36886 this.split.el.setHeight(box.height);
36888 if(this.collapsed){
36889 this.updateBody(null, box.height);
36891 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36894 Roo.namespace("Roo.bootstrap.panel");/*
36896 * Ext JS Library 1.1.1
36897 * Copyright(c) 2006-2007, Ext JS, LLC.
36899 * Originally Released Under LGPL - original licence link has changed is not relivant.
36902 * <script type="text/javascript">
36905 * @class Roo.ContentPanel
36906 * @extends Roo.util.Observable
36907 * A basic ContentPanel element.
36908 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36909 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36910 * @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
36911 * @cfg {Boolean} closable True if the panel can be closed/removed
36912 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36913 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36914 * @cfg {Toolbar} toolbar A toolbar for this panel
36915 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36916 * @cfg {String} title The title for this panel
36917 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36918 * @cfg {String} url Calls {@link #setUrl} with this value
36919 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36920 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36921 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36922 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36923 * @cfg {Boolean} badges render the badges
36926 * Create a new ContentPanel.
36927 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36928 * @param {String/Object} config A string to set only the title or a config object
36929 * @param {String} content (optional) Set the HTML content for this panel
36930 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36932 Roo.bootstrap.panel.Content = function( config){
36934 this.tpl = config.tpl || false;
36936 var el = config.el;
36937 var content = config.content;
36939 if(config.autoCreate){ // xtype is available if this is called from factory
36942 this.el = Roo.get(el);
36943 if(!this.el && config && config.autoCreate){
36944 if(typeof config.autoCreate == "object"){
36945 if(!config.autoCreate.id){
36946 config.autoCreate.id = config.id||el;
36948 this.el = Roo.DomHelper.append(document.body,
36949 config.autoCreate, true);
36951 var elcfg = { tag: "div",
36952 cls: "roo-layout-inactive-content",
36956 elcfg.html = config.html;
36960 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36963 this.closable = false;
36964 this.loaded = false;
36965 this.active = false;
36968 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36970 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36972 this.wrapEl = this.el; //this.el.wrap();
36974 if (config.toolbar.items) {
36975 ti = config.toolbar.items ;
36976 delete config.toolbar.items ;
36980 this.toolbar.render(this.wrapEl, 'before');
36981 for(var i =0;i < ti.length;i++) {
36982 // Roo.log(['add child', items[i]]);
36983 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36985 this.toolbar.items = nitems;
36986 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36987 delete config.toolbar;
36991 // xtype created footer. - not sure if will work as we normally have to render first..
36992 if (this.footer && !this.footer.el && this.footer.xtype) {
36993 if (!this.wrapEl) {
36994 this.wrapEl = this.el.wrap();
36997 this.footer.container = this.wrapEl.createChild();
36999 this.footer = Roo.factory(this.footer, Roo);
37004 if(typeof config == "string"){
37005 this.title = config;
37007 Roo.apply(this, config);
37011 this.resizeEl = Roo.get(this.resizeEl, true);
37013 this.resizeEl = this.el;
37015 // handle view.xtype
37023 * Fires when this panel is activated.
37024 * @param {Roo.ContentPanel} this
37028 * @event deactivate
37029 * Fires when this panel is activated.
37030 * @param {Roo.ContentPanel} this
37032 "deactivate" : true,
37036 * Fires when this panel is resized if fitToFrame is true.
37037 * @param {Roo.ContentPanel} this
37038 * @param {Number} width The width after any component adjustments
37039 * @param {Number} height The height after any component adjustments
37045 * Fires when this tab is created
37046 * @param {Roo.ContentPanel} this
37057 if(this.autoScroll){
37058 this.resizeEl.setStyle("overflow", "auto");
37060 // fix randome scrolling
37061 //this.el.on('scroll', function() {
37062 // Roo.log('fix random scolling');
37063 // this.scrollTo('top',0);
37066 content = content || this.content;
37068 this.setContent(content);
37070 if(config && config.url){
37071 this.setUrl(this.url, this.params, this.loadOnce);
37076 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37078 if (this.view && typeof(this.view.xtype) != 'undefined') {
37079 this.view.el = this.el.appendChild(document.createElement("div"));
37080 this.view = Roo.factory(this.view);
37081 this.view.render && this.view.render(false, '');
37085 this.fireEvent('render', this);
37088 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37092 setRegion : function(region){
37093 this.region = region;
37094 this.setActiveClass(region && !this.background);
37098 setActiveClass: function(state)
37101 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37102 this.el.setStyle('position','relative');
37104 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37105 this.el.setStyle('position', 'absolute');
37110 * Returns the toolbar for this Panel if one was configured.
37111 * @return {Roo.Toolbar}
37113 getToolbar : function(){
37114 return this.toolbar;
37117 setActiveState : function(active)
37119 this.active = active;
37120 this.setActiveClass(active);
37122 if(this.fireEvent("deactivate", this) === false){
37127 this.fireEvent("activate", this);
37131 * Updates this panel's element
37132 * @param {String} content The new content
37133 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37135 setContent : function(content, loadScripts){
37136 this.el.update(content, loadScripts);
37139 ignoreResize : function(w, h){
37140 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37143 this.lastSize = {width: w, height: h};
37148 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37149 * @return {Roo.UpdateManager} The UpdateManager
37151 getUpdateManager : function(){
37152 return this.el.getUpdateManager();
37155 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37156 * @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:
37159 url: "your-url.php",
37160 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37161 callback: yourFunction,
37162 scope: yourObject, //(optional scope)
37165 text: "Loading...",
37170 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37171 * 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.
37172 * @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}
37173 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37174 * @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.
37175 * @return {Roo.ContentPanel} this
37178 var um = this.el.getUpdateManager();
37179 um.update.apply(um, arguments);
37185 * 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.
37186 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37187 * @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)
37188 * @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)
37189 * @return {Roo.UpdateManager} The UpdateManager
37191 setUrl : function(url, params, loadOnce){
37192 if(this.refreshDelegate){
37193 this.removeListener("activate", this.refreshDelegate);
37195 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37196 this.on("activate", this.refreshDelegate);
37197 return this.el.getUpdateManager();
37200 _handleRefresh : function(url, params, loadOnce){
37201 if(!loadOnce || !this.loaded){
37202 var updater = this.el.getUpdateManager();
37203 updater.update(url, params, this._setLoaded.createDelegate(this));
37207 _setLoaded : function(){
37208 this.loaded = true;
37212 * Returns this panel's id
37215 getId : function(){
37220 * Returns this panel's element - used by regiosn to add.
37221 * @return {Roo.Element}
37223 getEl : function(){
37224 return this.wrapEl || this.el;
37229 adjustForComponents : function(width, height)
37231 //Roo.log('adjustForComponents ');
37232 if(this.resizeEl != this.el){
37233 width -= this.el.getFrameWidth('lr');
37234 height -= this.el.getFrameWidth('tb');
37237 var te = this.toolbar.getEl();
37238 te.setWidth(width);
37239 height -= te.getHeight();
37242 var te = this.footer.getEl();
37243 te.setWidth(width);
37244 height -= te.getHeight();
37248 if(this.adjustments){
37249 width += this.adjustments[0];
37250 height += this.adjustments[1];
37252 return {"width": width, "height": height};
37255 setSize : function(width, height){
37256 if(this.fitToFrame && !this.ignoreResize(width, height)){
37257 if(this.fitContainer && this.resizeEl != this.el){
37258 this.el.setSize(width, height);
37260 var size = this.adjustForComponents(width, height);
37261 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37262 this.fireEvent('resize', this, size.width, size.height);
37267 * Returns this panel's title
37270 getTitle : function(){
37272 if (typeof(this.title) != 'object') {
37277 for (var k in this.title) {
37278 if (!this.title.hasOwnProperty(k)) {
37282 if (k.indexOf('-') >= 0) {
37283 var s = k.split('-');
37284 for (var i = 0; i<s.length; i++) {
37285 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37288 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37295 * Set this panel's title
37296 * @param {String} title
37298 setTitle : function(title){
37299 this.title = title;
37301 this.region.updatePanelTitle(this, title);
37306 * Returns true is this panel was configured to be closable
37307 * @return {Boolean}
37309 isClosable : function(){
37310 return this.closable;
37313 beforeSlide : function(){
37315 this.resizeEl.clip();
37318 afterSlide : function(){
37320 this.resizeEl.unclip();
37324 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37325 * Will fail silently if the {@link #setUrl} method has not been called.
37326 * This does not activate the panel, just updates its content.
37328 refresh : function(){
37329 if(this.refreshDelegate){
37330 this.loaded = false;
37331 this.refreshDelegate();
37336 * Destroys this panel
37338 destroy : function(){
37339 this.el.removeAllListeners();
37340 var tempEl = document.createElement("span");
37341 tempEl.appendChild(this.el.dom);
37342 tempEl.innerHTML = "";
37348 * form - if the content panel contains a form - this is a reference to it.
37349 * @type {Roo.form.Form}
37353 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37354 * This contains a reference to it.
37360 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37370 * @param {Object} cfg Xtype definition of item to add.
37374 getChildContainer: function () {
37375 return this.getEl();
37380 var ret = new Roo.factory(cfg);
37385 if (cfg.xtype.match(/^Form$/)) {
37388 //if (this.footer) {
37389 // el = this.footer.container.insertSibling(false, 'before');
37391 el = this.el.createChild();
37394 this.form = new Roo.form.Form(cfg);
37397 if ( this.form.allItems.length) {
37398 this.form.render(el.dom);
37402 // should only have one of theses..
37403 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37404 // views.. should not be just added - used named prop 'view''
37406 cfg.el = this.el.appendChild(document.createElement("div"));
37409 var ret = new Roo.factory(cfg);
37411 ret.render && ret.render(false, ''); // render blank..
37421 * @class Roo.bootstrap.panel.Grid
37422 * @extends Roo.bootstrap.panel.Content
37424 * Create a new GridPanel.
37425 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37426 * @param {Object} config A the config object
37432 Roo.bootstrap.panel.Grid = function(config)
37436 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37437 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37439 config.el = this.wrapper;
37440 //this.el = this.wrapper;
37442 if (config.container) {
37443 // ctor'ed from a Border/panel.grid
37446 this.wrapper.setStyle("overflow", "hidden");
37447 this.wrapper.addClass('roo-grid-container');
37452 if(config.toolbar){
37453 var tool_el = this.wrapper.createChild();
37454 this.toolbar = Roo.factory(config.toolbar);
37456 if (config.toolbar.items) {
37457 ti = config.toolbar.items ;
37458 delete config.toolbar.items ;
37462 this.toolbar.render(tool_el);
37463 for(var i =0;i < ti.length;i++) {
37464 // Roo.log(['add child', items[i]]);
37465 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37467 this.toolbar.items = nitems;
37469 delete config.toolbar;
37472 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37473 config.grid.scrollBody = true;;
37474 config.grid.monitorWindowResize = false; // turn off autosizing
37475 config.grid.autoHeight = false;
37476 config.grid.autoWidth = false;
37478 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37480 if (config.background) {
37481 // render grid on panel activation (if panel background)
37482 this.on('activate', function(gp) {
37483 if (!gp.grid.rendered) {
37484 gp.grid.render(this.wrapper);
37485 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37490 this.grid.render(this.wrapper);
37491 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37494 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37495 // ??? needed ??? config.el = this.wrapper;
37500 // xtype created footer. - not sure if will work as we normally have to render first..
37501 if (this.footer && !this.footer.el && this.footer.xtype) {
37503 var ctr = this.grid.getView().getFooterPanel(true);
37504 this.footer.dataSource = this.grid.dataSource;
37505 this.footer = Roo.factory(this.footer, Roo);
37506 this.footer.render(ctr);
37516 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37517 getId : function(){
37518 return this.grid.id;
37522 * Returns the grid for this panel
37523 * @return {Roo.bootstrap.Table}
37525 getGrid : function(){
37529 setSize : function(width, height){
37530 if(!this.ignoreResize(width, height)){
37531 var grid = this.grid;
37532 var size = this.adjustForComponents(width, height);
37533 var gridel = grid.getGridEl();
37534 gridel.setSize(size.width, size.height);
37536 var thd = grid.getGridEl().select('thead',true).first();
37537 var tbd = grid.getGridEl().select('tbody', true).first();
37539 tbd.setSize(width, height - thd.getHeight());
37548 beforeSlide : function(){
37549 this.grid.getView().scroller.clip();
37552 afterSlide : function(){
37553 this.grid.getView().scroller.unclip();
37556 destroy : function(){
37557 this.grid.destroy();
37559 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37564 * @class Roo.bootstrap.panel.Nest
37565 * @extends Roo.bootstrap.panel.Content
37567 * Create a new Panel, that can contain a layout.Border.
37570 * @param {Roo.BorderLayout} layout The layout for this panel
37571 * @param {String/Object} config A string to set only the title or a config object
37573 Roo.bootstrap.panel.Nest = function(config)
37575 // construct with only one argument..
37576 /* FIXME - implement nicer consturctors
37577 if (layout.layout) {
37579 layout = config.layout;
37580 delete config.layout;
37582 if (layout.xtype && !layout.getEl) {
37583 // then layout needs constructing..
37584 layout = Roo.factory(layout, Roo);
37588 config.el = config.layout.getEl();
37590 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37592 config.layout.monitorWindowResize = false; // turn off autosizing
37593 this.layout = config.layout;
37594 this.layout.getEl().addClass("roo-layout-nested-layout");
37601 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37603 setSize : function(width, height){
37604 if(!this.ignoreResize(width, height)){
37605 var size = this.adjustForComponents(width, height);
37606 var el = this.layout.getEl();
37607 if (size.height < 1) {
37608 el.setWidth(size.width);
37610 el.setSize(size.width, size.height);
37612 var touch = el.dom.offsetWidth;
37613 this.layout.layout();
37614 // ie requires a double layout on the first pass
37615 if(Roo.isIE && !this.initialized){
37616 this.initialized = true;
37617 this.layout.layout();
37622 // activate all subpanels if not currently active..
37624 setActiveState : function(active){
37625 this.active = active;
37626 this.setActiveClass(active);
37629 this.fireEvent("deactivate", this);
37633 this.fireEvent("activate", this);
37634 // not sure if this should happen before or after..
37635 if (!this.layout) {
37636 return; // should not happen..
37639 for (var r in this.layout.regions) {
37640 reg = this.layout.getRegion(r);
37641 if (reg.getActivePanel()) {
37642 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37643 reg.setActivePanel(reg.getActivePanel());
37646 if (!reg.panels.length) {
37649 reg.showPanel(reg.getPanel(0));
37658 * Returns the nested BorderLayout for this panel
37659 * @return {Roo.BorderLayout}
37661 getLayout : function(){
37662 return this.layout;
37666 * Adds a xtype elements to the layout of the nested panel
37670 xtype : 'ContentPanel',
37677 xtype : 'NestedLayoutPanel',
37683 items : [ ... list of content panels or nested layout panels.. ]
37687 * @param {Object} cfg Xtype definition of item to add.
37689 addxtype : function(cfg) {
37690 return this.layout.addxtype(cfg);
37695 * Ext JS Library 1.1.1
37696 * Copyright(c) 2006-2007, Ext JS, LLC.
37698 * Originally Released Under LGPL - original licence link has changed is not relivant.
37701 * <script type="text/javascript">
37704 * @class Roo.TabPanel
37705 * @extends Roo.util.Observable
37706 * A lightweight tab container.
37710 // basic tabs 1, built from existing content
37711 var tabs = new Roo.TabPanel("tabs1");
37712 tabs.addTab("script", "View Script");
37713 tabs.addTab("markup", "View Markup");
37714 tabs.activate("script");
37716 // more advanced tabs, built from javascript
37717 var jtabs = new Roo.TabPanel("jtabs");
37718 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37720 // set up the UpdateManager
37721 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37722 var updater = tab2.getUpdateManager();
37723 updater.setDefaultUrl("ajax1.htm");
37724 tab2.on('activate', updater.refresh, updater, true);
37726 // Use setUrl for Ajax loading
37727 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37728 tab3.setUrl("ajax2.htm", null, true);
37731 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37734 jtabs.activate("jtabs-1");
37737 * Create a new TabPanel.
37738 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37739 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37741 Roo.bootstrap.panel.Tabs = function(config){
37743 * The container element for this TabPanel.
37744 * @type Roo.Element
37746 this.el = Roo.get(config.el);
37749 if(typeof config == "boolean"){
37750 this.tabPosition = config ? "bottom" : "top";
37752 Roo.apply(this, config);
37756 if(this.tabPosition == "bottom"){
37757 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37758 this.el.addClass("roo-tabs-bottom");
37760 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37761 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37762 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37764 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37766 if(this.tabPosition != "bottom"){
37767 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37768 * @type Roo.Element
37770 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37771 this.el.addClass("roo-tabs-top");
37775 this.bodyEl.setStyle("position", "relative");
37777 this.active = null;
37778 this.activateDelegate = this.activate.createDelegate(this);
37783 * Fires when the active tab changes
37784 * @param {Roo.TabPanel} this
37785 * @param {Roo.TabPanelItem} activePanel The new active tab
37789 * @event beforetabchange
37790 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37791 * @param {Roo.TabPanel} this
37792 * @param {Object} e Set cancel to true on this object to cancel the tab change
37793 * @param {Roo.TabPanelItem} tab The tab being changed to
37795 "beforetabchange" : true
37798 Roo.EventManager.onWindowResize(this.onResize, this);
37799 this.cpad = this.el.getPadding("lr");
37800 this.hiddenCount = 0;
37803 // toolbar on the tabbar support...
37804 if (this.toolbar) {
37805 alert("no toolbar support yet");
37806 this.toolbar = false;
37808 var tcfg = this.toolbar;
37809 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37810 this.toolbar = new Roo.Toolbar(tcfg);
37811 if (Roo.isSafari) {
37812 var tbl = tcfg.container.child('table', true);
37813 tbl.setAttribute('width', '100%');
37821 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37824 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37826 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37828 tabPosition : "top",
37830 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37832 currentTabWidth : 0,
37834 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37838 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37842 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37844 preferredTabWidth : 175,
37846 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37848 resizeTabs : false,
37850 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37852 monitorResize : true,
37854 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37859 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37860 * @param {String} id The id of the div to use <b>or create</b>
37861 * @param {String} text The text for the tab
37862 * @param {String} content (optional) Content to put in the TabPanelItem body
37863 * @param {Boolean} closable (optional) True to create a close icon on the tab
37864 * @return {Roo.TabPanelItem} The created TabPanelItem
37866 addTab : function(id, text, content, closable, tpl)
37868 var item = new Roo.bootstrap.panel.TabItem({
37872 closable : closable,
37875 this.addTabItem(item);
37877 item.setContent(content);
37883 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37884 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37885 * @return {Roo.TabPanelItem}
37887 getTab : function(id){
37888 return this.items[id];
37892 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37893 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37895 hideTab : function(id){
37896 var t = this.items[id];
37899 this.hiddenCount++;
37900 this.autoSizeTabs();
37905 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37906 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37908 unhideTab : function(id){
37909 var t = this.items[id];
37911 t.setHidden(false);
37912 this.hiddenCount--;
37913 this.autoSizeTabs();
37918 * Adds an existing {@link Roo.TabPanelItem}.
37919 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37921 addTabItem : function(item){
37922 this.items[item.id] = item;
37923 this.items.push(item);
37924 // if(this.resizeTabs){
37925 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37926 // this.autoSizeTabs();
37928 // item.autoSize();
37933 * Removes a {@link Roo.TabPanelItem}.
37934 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37936 removeTab : function(id){
37937 var items = this.items;
37938 var tab = items[id];
37939 if(!tab) { return; }
37940 var index = items.indexOf(tab);
37941 if(this.active == tab && items.length > 1){
37942 var newTab = this.getNextAvailable(index);
37947 this.stripEl.dom.removeChild(tab.pnode.dom);
37948 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37949 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37951 items.splice(index, 1);
37952 delete this.items[tab.id];
37953 tab.fireEvent("close", tab);
37954 tab.purgeListeners();
37955 this.autoSizeTabs();
37958 getNextAvailable : function(start){
37959 var items = this.items;
37961 // look for a next tab that will slide over to
37962 // replace the one being removed
37963 while(index < items.length){
37964 var item = items[++index];
37965 if(item && !item.isHidden()){
37969 // if one isn't found select the previous tab (on the left)
37972 var item = items[--index];
37973 if(item && !item.isHidden()){
37981 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37982 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37984 disableTab : function(id){
37985 var tab = this.items[id];
37986 if(tab && this.active != tab){
37992 * Enables a {@link Roo.TabPanelItem} that is disabled.
37993 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37995 enableTab : function(id){
37996 var tab = this.items[id];
38001 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38002 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38003 * @return {Roo.TabPanelItem} The TabPanelItem.
38005 activate : function(id){
38006 var tab = this.items[id];
38010 if(tab == this.active || tab.disabled){
38014 this.fireEvent("beforetabchange", this, e, tab);
38015 if(e.cancel !== true && !tab.disabled){
38017 this.active.hide();
38019 this.active = this.items[id];
38020 this.active.show();
38021 this.fireEvent("tabchange", this, this.active);
38027 * Gets the active {@link Roo.TabPanelItem}.
38028 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38030 getActiveTab : function(){
38031 return this.active;
38035 * Updates the tab body element to fit the height of the container element
38036 * for overflow scrolling
38037 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38039 syncHeight : function(targetHeight){
38040 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38041 var bm = this.bodyEl.getMargins();
38042 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38043 this.bodyEl.setHeight(newHeight);
38047 onResize : function(){
38048 if(this.monitorResize){
38049 this.autoSizeTabs();
38054 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38056 beginUpdate : function(){
38057 this.updating = true;
38061 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38063 endUpdate : function(){
38064 this.updating = false;
38065 this.autoSizeTabs();
38069 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38071 autoSizeTabs : function(){
38072 var count = this.items.length;
38073 var vcount = count - this.hiddenCount;
38074 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38077 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38078 var availWidth = Math.floor(w / vcount);
38079 var b = this.stripBody;
38080 if(b.getWidth() > w){
38081 var tabs = this.items;
38082 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38083 if(availWidth < this.minTabWidth){
38084 /*if(!this.sleft){ // incomplete scrolling code
38085 this.createScrollButtons();
38088 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38091 if(this.currentTabWidth < this.preferredTabWidth){
38092 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38098 * Returns the number of tabs in this TabPanel.
38101 getCount : function(){
38102 return this.items.length;
38106 * Resizes all the tabs to the passed width
38107 * @param {Number} The new width
38109 setTabWidth : function(width){
38110 this.currentTabWidth = width;
38111 for(var i = 0, len = this.items.length; i < len; i++) {
38112 if(!this.items[i].isHidden()) {
38113 this.items[i].setWidth(width);
38119 * Destroys this TabPanel
38120 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38122 destroy : function(removeEl){
38123 Roo.EventManager.removeResizeListener(this.onResize, this);
38124 for(var i = 0, len = this.items.length; i < len; i++){
38125 this.items[i].purgeListeners();
38127 if(removeEl === true){
38128 this.el.update("");
38133 createStrip : function(container)
38135 var strip = document.createElement("nav");
38136 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38137 container.appendChild(strip);
38141 createStripList : function(strip)
38143 // div wrapper for retard IE
38144 // returns the "tr" element.
38145 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38146 //'<div class="x-tabs-strip-wrap">'+
38147 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38148 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38149 return strip.firstChild; //.firstChild.firstChild.firstChild;
38151 createBody : function(container)
38153 var body = document.createElement("div");
38154 Roo.id(body, "tab-body");
38155 //Roo.fly(body).addClass("x-tabs-body");
38156 Roo.fly(body).addClass("tab-content");
38157 container.appendChild(body);
38160 createItemBody :function(bodyEl, id){
38161 var body = Roo.getDom(id);
38163 body = document.createElement("div");
38166 //Roo.fly(body).addClass("x-tabs-item-body");
38167 Roo.fly(body).addClass("tab-pane");
38168 bodyEl.insertBefore(body, bodyEl.firstChild);
38172 createStripElements : function(stripEl, text, closable, tpl)
38174 var td = document.createElement("li"); // was td..
38177 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38180 stripEl.appendChild(td);
38182 td.className = "x-tabs-closable";
38183 if(!this.closeTpl){
38184 this.closeTpl = new Roo.Template(
38185 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38186 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38187 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38190 var el = this.closeTpl.overwrite(td, {"text": text});
38191 var close = el.getElementsByTagName("div")[0];
38192 var inner = el.getElementsByTagName("em")[0];
38193 return {"el": el, "close": close, "inner": inner};
38196 // not sure what this is..
38197 // if(!this.tabTpl){
38198 //this.tabTpl = new Roo.Template(
38199 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38200 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38202 // this.tabTpl = new Roo.Template(
38203 // '<a href="#">' +
38204 // '<span unselectable="on"' +
38205 // (this.disableTooltips ? '' : ' title="{text}"') +
38206 // ' >{text}</span></a>'
38212 var template = tpl || this.tabTpl || false;
38216 template = new Roo.Template(
38218 '<span unselectable="on"' +
38219 (this.disableTooltips ? '' : ' title="{text}"') +
38220 ' >{text}</span></a>'
38224 switch (typeof(template)) {
38228 template = new Roo.Template(template);
38234 var el = template.overwrite(td, {"text": text});
38236 var inner = el.getElementsByTagName("span")[0];
38238 return {"el": el, "inner": inner};
38246 * @class Roo.TabPanelItem
38247 * @extends Roo.util.Observable
38248 * Represents an individual item (tab plus body) in a TabPanel.
38249 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38250 * @param {String} id The id of this TabPanelItem
38251 * @param {String} text The text for the tab of this TabPanelItem
38252 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38254 Roo.bootstrap.panel.TabItem = function(config){
38256 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38257 * @type Roo.TabPanel
38259 this.tabPanel = config.panel;
38261 * The id for this TabPanelItem
38264 this.id = config.id;
38266 this.disabled = false;
38268 this.text = config.text;
38270 this.loaded = false;
38271 this.closable = config.closable;
38274 * The body element for this TabPanelItem.
38275 * @type Roo.Element
38277 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38278 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38279 this.bodyEl.setStyle("display", "block");
38280 this.bodyEl.setStyle("zoom", "1");
38281 //this.hideAction();
38283 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38285 this.el = Roo.get(els.el);
38286 this.inner = Roo.get(els.inner, true);
38287 this.textEl = Roo.get(this.el.dom.firstChild, true);
38288 this.pnode = Roo.get(els.el.parentNode, true);
38289 // this.el.on("mousedown", this.onTabMouseDown, this);
38290 this.el.on("click", this.onTabClick, this);
38292 if(config.closable){
38293 var c = Roo.get(els.close, true);
38294 c.dom.title = this.closeText;
38295 c.addClassOnOver("close-over");
38296 c.on("click", this.closeClick, this);
38302 * Fires when this tab becomes the active tab.
38303 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38304 * @param {Roo.TabPanelItem} this
38308 * @event beforeclose
38309 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38310 * @param {Roo.TabPanelItem} this
38311 * @param {Object} e Set cancel to true on this object to cancel the close.
38313 "beforeclose": true,
38316 * Fires when this tab is closed.
38317 * @param {Roo.TabPanelItem} this
38321 * @event deactivate
38322 * Fires when this tab is no longer the active tab.
38323 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38324 * @param {Roo.TabPanelItem} this
38326 "deactivate" : true
38328 this.hidden = false;
38330 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38333 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38335 purgeListeners : function(){
38336 Roo.util.Observable.prototype.purgeListeners.call(this);
38337 this.el.removeAllListeners();
38340 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38343 this.pnode.addClass("active");
38346 this.tabPanel.stripWrap.repaint();
38348 this.fireEvent("activate", this.tabPanel, this);
38352 * Returns true if this tab is the active tab.
38353 * @return {Boolean}
38355 isActive : function(){
38356 return this.tabPanel.getActiveTab() == this;
38360 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38363 this.pnode.removeClass("active");
38365 this.fireEvent("deactivate", this.tabPanel, this);
38368 hideAction : function(){
38369 this.bodyEl.hide();
38370 this.bodyEl.setStyle("position", "absolute");
38371 this.bodyEl.setLeft("-20000px");
38372 this.bodyEl.setTop("-20000px");
38375 showAction : function(){
38376 this.bodyEl.setStyle("position", "relative");
38377 this.bodyEl.setTop("");
38378 this.bodyEl.setLeft("");
38379 this.bodyEl.show();
38383 * Set the tooltip for the tab.
38384 * @param {String} tooltip The tab's tooltip
38386 setTooltip : function(text){
38387 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38388 this.textEl.dom.qtip = text;
38389 this.textEl.dom.removeAttribute('title');
38391 this.textEl.dom.title = text;
38395 onTabClick : function(e){
38396 e.preventDefault();
38397 this.tabPanel.activate(this.id);
38400 onTabMouseDown : function(e){
38401 e.preventDefault();
38402 this.tabPanel.activate(this.id);
38405 getWidth : function(){
38406 return this.inner.getWidth();
38409 setWidth : function(width){
38410 var iwidth = width - this.pnode.getPadding("lr");
38411 this.inner.setWidth(iwidth);
38412 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38413 this.pnode.setWidth(width);
38417 * Show or hide the tab
38418 * @param {Boolean} hidden True to hide or false to show.
38420 setHidden : function(hidden){
38421 this.hidden = hidden;
38422 this.pnode.setStyle("display", hidden ? "none" : "");
38426 * Returns true if this tab is "hidden"
38427 * @return {Boolean}
38429 isHidden : function(){
38430 return this.hidden;
38434 * Returns the text for this tab
38437 getText : function(){
38441 autoSize : function(){
38442 //this.el.beginMeasure();
38443 this.textEl.setWidth(1);
38445 * #2804 [new] Tabs in Roojs
38446 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38448 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38449 //this.el.endMeasure();
38453 * Sets the text for the tab (Note: this also sets the tooltip text)
38454 * @param {String} text The tab's text and tooltip
38456 setText : function(text){
38458 this.textEl.update(text);
38459 this.setTooltip(text);
38460 //if(!this.tabPanel.resizeTabs){
38461 // this.autoSize();
38465 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38467 activate : function(){
38468 this.tabPanel.activate(this.id);
38472 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38474 disable : function(){
38475 if(this.tabPanel.active != this){
38476 this.disabled = true;
38477 this.pnode.addClass("disabled");
38482 * Enables this TabPanelItem if it was previously disabled.
38484 enable : function(){
38485 this.disabled = false;
38486 this.pnode.removeClass("disabled");
38490 * Sets the content for this TabPanelItem.
38491 * @param {String} content The content
38492 * @param {Boolean} loadScripts true to look for and load scripts
38494 setContent : function(content, loadScripts){
38495 this.bodyEl.update(content, loadScripts);
38499 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38500 * @return {Roo.UpdateManager} The UpdateManager
38502 getUpdateManager : function(){
38503 return this.bodyEl.getUpdateManager();
38507 * Set a URL to be used to load the content for this TabPanelItem.
38508 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38509 * @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)
38510 * @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)
38511 * @return {Roo.UpdateManager} The UpdateManager
38513 setUrl : function(url, params, loadOnce){
38514 if(this.refreshDelegate){
38515 this.un('activate', this.refreshDelegate);
38517 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38518 this.on("activate", this.refreshDelegate);
38519 return this.bodyEl.getUpdateManager();
38523 _handleRefresh : function(url, params, loadOnce){
38524 if(!loadOnce || !this.loaded){
38525 var updater = this.bodyEl.getUpdateManager();
38526 updater.update(url, params, this._setLoaded.createDelegate(this));
38531 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38532 * Will fail silently if the setUrl method has not been called.
38533 * This does not activate the panel, just updates its content.
38535 refresh : function(){
38536 if(this.refreshDelegate){
38537 this.loaded = false;
38538 this.refreshDelegate();
38543 _setLoaded : function(){
38544 this.loaded = true;
38548 closeClick : function(e){
38551 this.fireEvent("beforeclose", this, o);
38552 if(o.cancel !== true){
38553 this.tabPanel.removeTab(this.id);
38557 * The text displayed in the tooltip for the close icon.
38560 closeText : "Close this tab"
38563 * This script refer to:
38564 * Title: International Telephone Input
38565 * Author: Jack O'Connor
38566 * Code version: v12.1.12
38567 * Availability: https://github.com/jackocnr/intl-tel-input.git
38570 Roo.bootstrap.PhoneInputData = function() {
38573 "Afghanistan (افغانستان)",
38578 "Albania (Shqipëri)",
38583 "Algeria (الجزائر)",
38608 "Antigua and Barbuda",
38618 "Armenia (Հայաստան)",
38634 "Austria (Österreich)",
38639 "Azerbaijan (Azərbaycan)",
38649 "Bahrain (البحرين)",
38654 "Bangladesh (বাংলাদেশ)",
38664 "Belarus (Беларусь)",
38669 "Belgium (België)",
38699 "Bosnia and Herzegovina (Босна и Херцеговина)",
38714 "British Indian Ocean Territory",
38719 "British Virgin Islands",
38729 "Bulgaria (България)",
38739 "Burundi (Uburundi)",
38744 "Cambodia (កម្ពុជា)",
38749 "Cameroon (Cameroun)",
38758 ["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"]
38761 "Cape Verde (Kabu Verdi)",
38766 "Caribbean Netherlands",
38777 "Central African Republic (République centrafricaine)",
38797 "Christmas Island",
38803 "Cocos (Keeling) Islands",
38814 "Comoros (جزر القمر)",
38819 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38824 "Congo (Republic) (Congo-Brazzaville)",
38844 "Croatia (Hrvatska)",
38865 "Czech Republic (Česká republika)",
38870 "Denmark (Danmark)",
38885 "Dominican Republic (República Dominicana)",
38889 ["809", "829", "849"]
38907 "Equatorial Guinea (Guinea Ecuatorial)",
38927 "Falkland Islands (Islas Malvinas)",
38932 "Faroe Islands (Føroyar)",
38953 "French Guiana (Guyane française)",
38958 "French Polynesia (Polynésie française)",
38973 "Georgia (საქართველო)",
38978 "Germany (Deutschland)",
38998 "Greenland (Kalaallit Nunaat)",
39035 "Guinea-Bissau (Guiné Bissau)",
39060 "Hungary (Magyarország)",
39065 "Iceland (Ísland)",
39085 "Iraq (العراق)",
39101 "Israel (ישראל)",
39128 "Jordan (الأردن)",
39133 "Kazakhstan (Казахстан)",
39154 "Kuwait (الكويت)",
39159 "Kyrgyzstan (Кыргызстан)",
39169 "Latvia (Latvija)",
39174 "Lebanon (لبنان)",
39189 "Libya (ليبيا)",
39199 "Lithuania (Lietuva)",
39214 "Macedonia (FYROM) (Македонија)",
39219 "Madagascar (Madagasikara)",
39249 "Marshall Islands",
39259 "Mauritania (موريتانيا)",
39264 "Mauritius (Moris)",
39285 "Moldova (Republica Moldova)",
39295 "Mongolia (Монгол)",
39300 "Montenegro (Crna Gora)",
39310 "Morocco (المغرب)",
39316 "Mozambique (Moçambique)",
39321 "Myanmar (Burma) (မြန်မာ)",
39326 "Namibia (Namibië)",
39341 "Netherlands (Nederland)",
39346 "New Caledonia (Nouvelle-Calédonie)",
39381 "North Korea (조선 민주주의 인민 공화국)",
39386 "Northern Mariana Islands",
39402 "Pakistan (پاکستان)",
39412 "Palestine (فلسطين)",
39422 "Papua New Guinea",
39464 "Réunion (La Réunion)",
39470 "Romania (România)",
39486 "Saint Barthélemy",
39497 "Saint Kitts and Nevis",
39507 "Saint Martin (Saint-Martin (partie française))",
39513 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39518 "Saint Vincent and the Grenadines",
39533 "São Tomé and Príncipe (São Tomé e Príncipe)",
39538 "Saudi Arabia (المملكة العربية السعودية)",
39543 "Senegal (Sénégal)",
39573 "Slovakia (Slovensko)",
39578 "Slovenia (Slovenija)",
39588 "Somalia (Soomaaliya)",
39598 "South Korea (대한민국)",
39603 "South Sudan (جنوب السودان)",
39613 "Sri Lanka (ශ්රී ලංකාව)",
39618 "Sudan (السودان)",
39628 "Svalbard and Jan Mayen",
39639 "Sweden (Sverige)",
39644 "Switzerland (Schweiz)",
39649 "Syria (سوريا)",
39694 "Trinidad and Tobago",
39699 "Tunisia (تونس)",
39704 "Turkey (Türkiye)",
39714 "Turks and Caicos Islands",
39724 "U.S. Virgin Islands",
39734 "Ukraine (Україна)",
39739 "United Arab Emirates (الإمارات العربية المتحدة)",
39761 "Uzbekistan (Oʻzbekiston)",
39771 "Vatican City (Città del Vaticano)",
39782 "Vietnam (Việt Nam)",
39787 "Wallis and Futuna (Wallis-et-Futuna)",
39792 "Western Sahara (الصحراء الغربية)",
39798 "Yemen (اليمن)",
39822 * This script refer to:
39823 * Title: International Telephone Input
39824 * Author: Jack O'Connor
39825 * Code version: v12.1.12
39826 * Availability: https://github.com/jackocnr/intl-tel-input.git
39830 * @class Roo.bootstrap.PhoneInput
39831 * @extends Roo.bootstrap.TriggerField
39832 * An input with International dial-code selection
39834 * @cfg {String} defaultDialCode default '+852'
39835 * @cfg {Array} preferedCountries default []
39838 * Create a new PhoneInput.
39839 * @param {Object} config Configuration options
39842 Roo.bootstrap.PhoneInput = function(config) {
39843 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39846 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39848 listWidth: undefined,
39850 selectedClass: 'active',
39852 invalidClass : "has-warning",
39854 validClass: 'has-success',
39856 allowed: '0123456789',
39859 * @cfg {String} defaultDialCode The default dial code when initializing the input
39861 defaultDialCode: '+852',
39864 * @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
39866 preferedCountries: false,
39868 getAutoCreate : function()
39870 var data = Roo.bootstrap.PhoneInputData();
39871 var align = this.labelAlign || this.parentLabelAlign();
39874 this.allCountries = [];
39875 this.dialCodeMapping = [];
39877 for (var i = 0; i < data.length; i++) {
39879 this.allCountries[i] = {
39883 priority: c[3] || 0,
39884 areaCodes: c[4] || null
39886 this.dialCodeMapping[c[2]] = {
39889 priority: c[3] || 0,
39890 areaCodes: c[4] || null
39902 cls : 'form-control tel-input',
39903 autocomplete: 'new-password'
39906 var hiddenInput = {
39909 cls: 'hidden-tel-input'
39913 hiddenInput.name = this.name;
39916 if (this.disabled) {
39917 input.disabled = true;
39920 var flag_container = {
39937 cls: this.hasFeedback ? 'has-feedback' : '',
39943 cls: 'dial-code-holder',
39950 cls: 'roo-select2-container input-group',
39957 if (this.fieldLabel.length) {
39960 tooltip: 'This field is required'
39966 cls: 'control-label',
39972 html: this.fieldLabel
39975 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39981 if(this.indicatorpos == 'right') {
39982 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39989 if(align == 'left') {
39997 if(this.labelWidth > 12){
39998 label.style = "width: " + this.labelWidth + 'px';
40000 if(this.labelWidth < 13 && this.labelmd == 0){
40001 this.labelmd = this.labelWidth;
40003 if(this.labellg > 0){
40004 label.cls += ' col-lg-' + this.labellg;
40005 input.cls += ' col-lg-' + (12 - this.labellg);
40007 if(this.labelmd > 0){
40008 label.cls += ' col-md-' + this.labelmd;
40009 container.cls += ' col-md-' + (12 - this.labelmd);
40011 if(this.labelsm > 0){
40012 label.cls += ' col-sm-' + this.labelsm;
40013 container.cls += ' col-sm-' + (12 - this.labelsm);
40015 if(this.labelxs > 0){
40016 label.cls += ' col-xs-' + this.labelxs;
40017 container.cls += ' col-xs-' + (12 - this.labelxs);
40027 var settings = this;
40029 ['xs','sm','md','lg'].map(function(size){
40030 if (settings[size]) {
40031 cfg.cls += ' col-' + size + '-' + settings[size];
40035 this.store = new Roo.data.Store({
40036 proxy : new Roo.data.MemoryProxy({}),
40037 reader : new Roo.data.JsonReader({
40048 'name' : 'dialCode',
40052 'name' : 'priority',
40056 'name' : 'areaCodes',
40063 if(!this.preferedCountries) {
40064 this.preferedCountries = [
40071 var p = this.preferedCountries.reverse();
40074 for (var i = 0; i < p.length; i++) {
40075 for (var j = 0; j < this.allCountries.length; j++) {
40076 if(this.allCountries[j].iso2 == p[i]) {
40077 var t = this.allCountries[j];
40078 this.allCountries.splice(j,1);
40079 this.allCountries.unshift(t);
40085 this.store.proxy.data = {
40087 data: this.allCountries
40093 initEvents : function()
40096 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40098 this.indicator = this.indicatorEl();
40099 this.flag = this.flagEl();
40100 this.dialCodeHolder = this.dialCodeHolderEl();
40102 this.trigger = this.el.select('div.flag-box',true).first();
40103 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40108 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40109 _this.list.setWidth(lw);
40112 this.list.on('mouseover', this.onViewOver, this);
40113 this.list.on('mousemove', this.onViewMove, this);
40114 this.inputEl().on("keyup", this.onKeyUp, this);
40116 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40118 this.view = new Roo.View(this.list, this.tpl, {
40119 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40122 this.view.on('click', this.onViewClick, this);
40123 this.setValue(this.defaultDialCode);
40126 onTriggerClick : function(e)
40128 Roo.log('trigger click');
40133 if(this.isExpanded()){
40135 this.hasFocus = false;
40137 this.store.load({});
40138 this.hasFocus = true;
40143 isExpanded : function()
40145 return this.list.isVisible();
40148 collapse : function()
40150 if(!this.isExpanded()){
40154 Roo.get(document).un('mousedown', this.collapseIf, this);
40155 Roo.get(document).un('mousewheel', this.collapseIf, this);
40156 this.fireEvent('collapse', this);
40160 expand : function()
40164 if(this.isExpanded() || !this.hasFocus){
40168 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40169 this.list.setWidth(lw);
40172 this.restrictHeight();
40174 Roo.get(document).on('mousedown', this.collapseIf, this);
40175 Roo.get(document).on('mousewheel', this.collapseIf, this);
40177 this.fireEvent('expand', this);
40180 restrictHeight : function()
40182 this.list.alignTo(this.inputEl(), this.listAlign);
40183 this.list.alignTo(this.inputEl(), this.listAlign);
40186 onViewOver : function(e, t)
40188 if(this.inKeyMode){
40191 var item = this.view.findItemFromChild(t);
40194 var index = this.view.indexOf(item);
40195 this.select(index, false);
40200 onViewClick : function(view, doFocus, el, e)
40202 var index = this.view.getSelectedIndexes()[0];
40204 var r = this.store.getAt(index);
40207 this.onSelect(r, index);
40209 if(doFocus !== false && !this.blockFocus){
40210 this.inputEl().focus();
40214 onViewMove : function(e, t)
40216 this.inKeyMode = false;
40219 select : function(index, scrollIntoView)
40221 this.selectedIndex = index;
40222 this.view.select(index);
40223 if(scrollIntoView !== false){
40224 var el = this.view.getNode(index);
40226 this.list.scrollChildIntoView(el, false);
40231 createList : function()
40233 this.list = Roo.get(document.body).createChild({
40235 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40236 style: 'display:none'
40239 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40242 collapseIf : function(e)
40244 var in_combo = e.within(this.el);
40245 var in_list = e.within(this.list);
40246 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40248 if (in_combo || in_list || is_list) {
40254 onSelect : function(record, index)
40256 if(this.fireEvent('beforeselect', this, record, index) !== false){
40258 this.setFlagClass(record.data.iso2);
40259 this.setDialCode(record.data.dialCode);
40260 this.hasFocus = false;
40262 this.fireEvent('select', this, record, index);
40266 flagEl : function()
40268 var flag = this.el.select('div.flag',true).first();
40275 dialCodeHolderEl : function()
40277 var d = this.el.select('input.dial-code-holder',true).first();
40284 setDialCode : function(v)
40286 this.dialCodeHolder.dom.value = '+'+v;
40289 setFlagClass : function(n)
40291 this.flag.dom.className = 'flag '+n;
40294 getValue : function()
40296 var v = this.inputEl().getValue();
40297 if(this.dialCodeHolder) {
40298 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40303 setValue : function(v)
40305 var d = this.getDialCode(v);
40307 //invalid dial code
40308 if(v.length == 0 || !d || d.length == 0) {
40310 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40311 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40317 this.setFlagClass(this.dialCodeMapping[d].iso2);
40318 this.setDialCode(d);
40319 this.inputEl().dom.value = v.replace('+'+d,'');
40320 this.hiddenEl().dom.value = this.getValue();
40325 getDialCode : function(v)
40329 if (v.length == 0) {
40330 return this.dialCodeHolder.dom.value;
40334 if (v.charAt(0) != "+") {
40337 var numericChars = "";
40338 for (var i = 1; i < v.length; i++) {
40339 var c = v.charAt(i);
40342 if (this.dialCodeMapping[numericChars]) {
40343 dialCode = v.substr(1, i);
40345 if (numericChars.length == 4) {
40355 this.setValue(this.defaultDialCode);
40359 hiddenEl : function()
40361 return this.el.select('input.hidden-tel-input',true).first();
40364 onKeyUp : function(e){
40366 var k = e.getKey();
40367 var c = e.getCharCode();
40370 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40371 this.allowed.indexOf(String.fromCharCode(c)) === -1
40376 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40379 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40383 this.setValue(this.getValue());
40388 * @class Roo.bootstrap.MoneyField
40389 * @extends Roo.bootstrap.ComboBox
40390 * Bootstrap MoneyField class
40393 * Create a new MoneyField.
40394 * @param {Object} config Configuration options
40397 Roo.bootstrap.MoneyField = function(config) {
40399 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40403 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40406 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40408 allowDecimals : true,
40410 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40412 decimalSeparator : ".",
40414 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40416 decimalPrecision : 0,
40418 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40420 allowNegative : true,
40422 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40426 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40428 minValue : Number.NEGATIVE_INFINITY,
40430 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40432 maxValue : Number.MAX_VALUE,
40434 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40436 minText : "The minimum value for this field is {0}",
40438 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40440 maxText : "The maximum value for this field is {0}",
40442 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40443 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40445 nanText : "{0} is not a valid number",
40447 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40451 * @cfg {String} defaults currency of the MoneyField
40452 * value should be in lkey
40454 defaultCurrency : false,
40456 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40458 thousandsDelimiter : false,
40468 getAutoCreate : function()
40470 var align = this.labelAlign || this.parentLabelAlign();
40482 cls : 'form-control roo-money-amount-input',
40483 autocomplete: 'new-password'
40486 var hiddenInput = {
40490 cls: 'hidden-number-input'
40494 hiddenInput.name = this.name;
40497 if (this.disabled) {
40498 input.disabled = true;
40501 var clg = 12 - this.inputlg;
40502 var cmd = 12 - this.inputmd;
40503 var csm = 12 - this.inputsm;
40504 var cxs = 12 - this.inputxs;
40508 cls : 'row roo-money-field',
40512 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40516 cls: 'roo-select2-container input-group',
40520 cls : 'form-control roo-money-currency-input',
40521 autocomplete: 'new-password',
40523 name : this.currencyName
40527 cls : 'input-group-addon',
40541 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40545 cls: this.hasFeedback ? 'has-feedback' : '',
40556 if (this.fieldLabel.length) {
40559 tooltip: 'This field is required'
40565 cls: 'control-label',
40571 html: this.fieldLabel
40574 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40580 if(this.indicatorpos == 'right') {
40581 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40588 if(align == 'left') {
40596 if(this.labelWidth > 12){
40597 label.style = "width: " + this.labelWidth + 'px';
40599 if(this.labelWidth < 13 && this.labelmd == 0){
40600 this.labelmd = this.labelWidth;
40602 if(this.labellg > 0){
40603 label.cls += ' col-lg-' + this.labellg;
40604 input.cls += ' col-lg-' + (12 - this.labellg);
40606 if(this.labelmd > 0){
40607 label.cls += ' col-md-' + this.labelmd;
40608 container.cls += ' col-md-' + (12 - this.labelmd);
40610 if(this.labelsm > 0){
40611 label.cls += ' col-sm-' + this.labelsm;
40612 container.cls += ' col-sm-' + (12 - this.labelsm);
40614 if(this.labelxs > 0){
40615 label.cls += ' col-xs-' + this.labelxs;
40616 container.cls += ' col-xs-' + (12 - this.labelxs);
40627 var settings = this;
40629 ['xs','sm','md','lg'].map(function(size){
40630 if (settings[size]) {
40631 cfg.cls += ' col-' + size + '-' + settings[size];
40638 initEvents : function()
40640 this.indicator = this.indicatorEl();
40642 this.initCurrencyEvent();
40644 this.initNumberEvent();
40647 initCurrencyEvent : function()
40650 throw "can not find store for combo";
40653 this.store = Roo.factory(this.store, Roo.data);
40654 this.store.parent = this;
40658 this.triggerEl = this.el.select('.input-group-addon', true).first();
40660 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40665 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40666 _this.list.setWidth(lw);
40669 this.list.on('mouseover', this.onViewOver, this);
40670 this.list.on('mousemove', this.onViewMove, this);
40671 this.list.on('scroll', this.onViewScroll, this);
40674 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40677 this.view = new Roo.View(this.list, this.tpl, {
40678 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40681 this.view.on('click', this.onViewClick, this);
40683 this.store.on('beforeload', this.onBeforeLoad, this);
40684 this.store.on('load', this.onLoad, this);
40685 this.store.on('loadexception', this.onLoadException, this);
40687 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40688 "up" : function(e){
40689 this.inKeyMode = true;
40693 "down" : function(e){
40694 if(!this.isExpanded()){
40695 this.onTriggerClick();
40697 this.inKeyMode = true;
40702 "enter" : function(e){
40705 if(this.fireEvent("specialkey", this, e)){
40706 this.onViewClick(false);
40712 "esc" : function(e){
40716 "tab" : function(e){
40719 if(this.fireEvent("specialkey", this, e)){
40720 this.onViewClick(false);
40728 doRelay : function(foo, bar, hname){
40729 if(hname == 'down' || this.scope.isExpanded()){
40730 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40738 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40742 initNumberEvent : function(e)
40744 this.inputEl().on("keydown" , this.fireKey, this);
40745 this.inputEl().on("focus", this.onFocus, this);
40746 this.inputEl().on("blur", this.onBlur, this);
40748 this.inputEl().relayEvent('keyup', this);
40750 if(this.indicator){
40751 this.indicator.addClass('invisible');
40754 this.originalValue = this.getValue();
40756 if(this.validationEvent == 'keyup'){
40757 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40758 this.inputEl().on('keyup', this.filterValidation, this);
40760 else if(this.validationEvent !== false){
40761 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40764 if(this.selectOnFocus){
40765 this.on("focus", this.preFocus, this);
40768 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40769 this.inputEl().on("keypress", this.filterKeys, this);
40771 this.inputEl().relayEvent('keypress', this);
40774 var allowed = "0123456789";
40776 if(this.allowDecimals){
40777 allowed += this.decimalSeparator;
40780 if(this.allowNegative){
40784 if(this.thousandsDelimiter) {
40788 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40790 var keyPress = function(e){
40792 var k = e.getKey();
40794 var c = e.getCharCode();
40797 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40798 allowed.indexOf(String.fromCharCode(c)) === -1
40804 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40808 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40813 this.inputEl().on("keypress", keyPress, this);
40817 onTriggerClick : function(e)
40824 this.loadNext = false;
40826 if(this.isExpanded()){
40831 this.hasFocus = true;
40833 if(this.triggerAction == 'all') {
40834 this.doQuery(this.allQuery, true);
40838 this.doQuery(this.getRawValue());
40841 getCurrency : function()
40843 var v = this.currencyEl().getValue();
40848 restrictHeight : function()
40850 this.list.alignTo(this.currencyEl(), this.listAlign);
40851 this.list.alignTo(this.currencyEl(), this.listAlign);
40854 onViewClick : function(view, doFocus, el, e)
40856 var index = this.view.getSelectedIndexes()[0];
40858 var r = this.store.getAt(index);
40861 this.onSelect(r, index);
40865 onSelect : function(record, index){
40867 if(this.fireEvent('beforeselect', this, record, index) !== false){
40869 this.setFromCurrencyData(index > -1 ? record.data : false);
40873 this.fireEvent('select', this, record, index);
40877 setFromCurrencyData : function(o)
40881 this.lastCurrency = o;
40883 if (this.currencyField) {
40884 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40886 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40889 this.lastSelectionText = currency;
40891 //setting default currency
40892 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40893 this.setCurrency(this.defaultCurrency);
40897 this.setCurrency(currency);
40900 setFromData : function(o)
40904 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40906 this.setFromCurrencyData(c);
40911 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40913 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40916 this.setValue(value);
40920 setCurrency : function(v)
40922 this.currencyValue = v;
40925 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40930 setValue : function(v)
40932 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40938 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40940 this.inputEl().dom.value = (v == '') ? '' :
40941 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40943 if(!this.allowZero && v === '0') {
40944 this.hiddenEl().dom.value = '';
40945 this.inputEl().dom.value = '';
40952 getRawValue : function()
40954 var v = this.inputEl().getValue();
40959 getValue : function()
40961 return this.fixPrecision(this.parseValue(this.getRawValue()));
40964 parseValue : function(value)
40966 if(this.thousandsDelimiter) {
40968 r = new RegExp(",", "g");
40969 value = value.replace(r, "");
40972 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40973 return isNaN(value) ? '' : value;
40977 fixPrecision : function(value)
40979 if(this.thousandsDelimiter) {
40981 r = new RegExp(",", "g");
40982 value = value.replace(r, "");
40985 var nan = isNaN(value);
40987 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40988 return nan ? '' : value;
40990 return parseFloat(value).toFixed(this.decimalPrecision);
40993 decimalPrecisionFcn : function(v)
40995 return Math.floor(v);
40998 validateValue : function(value)
41000 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41004 var num = this.parseValue(value);
41007 this.markInvalid(String.format(this.nanText, value));
41011 if(num < this.minValue){
41012 this.markInvalid(String.format(this.minText, this.minValue));
41016 if(num > this.maxValue){
41017 this.markInvalid(String.format(this.maxText, this.maxValue));
41024 validate : function()
41026 if(this.disabled || this.allowBlank){
41031 var currency = this.getCurrency();
41033 if(this.validateValue(this.getRawValue()) && currency.length){
41038 this.markInvalid();
41042 getName: function()
41047 beforeBlur : function()
41053 var v = this.parseValue(this.getRawValue());
41060 onBlur : function()
41064 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41065 //this.el.removeClass(this.focusClass);
41068 this.hasFocus = false;
41070 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41074 var v = this.getValue();
41076 if(String(v) !== String(this.startValue)){
41077 this.fireEvent('change', this, v, this.startValue);
41080 this.fireEvent("blur", this);
41083 inputEl : function()
41085 return this.el.select('.roo-money-amount-input', true).first();
41088 currencyEl : function()
41090 return this.el.select('.roo-money-currency-input', true).first();
41093 hiddenEl : function()
41095 return this.el.select('input.hidden-number-input',true).first();