4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
21 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
24 * Do not use directly - it does not do anything..
25 * @param {Object} config The config object
30 Roo.bootstrap.Component = function(config){
31 Roo.bootstrap.Component.superclass.constructor.call(this, config);
35 * @event childrenrendered
36 * Fires when the children have been rendered..
37 * @param {Roo.bootstrap.Component} this
39 "childrenrendered" : true
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
51 allowDomMove : false, // to stop relocations in parent onRender...
61 * Initialize Events for the element
63 initEvents : function() { },
69 can_build_overlaid : true,
71 container_method : false,
78 // returns the parent component..
79 return Roo.ComponentMgr.get(this.parentId)
85 onRender : function(ct, position)
87 // Roo.log("Call onRender: " + this.xtype);
89 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
92 if (this.el.attr('xtype')) {
93 this.el.attr('xtypex', this.el.attr('xtype'));
94 this.el.dom.removeAttribute('xtype');
104 var cfg = Roo.apply({}, this.getAutoCreate());
106 cfg.id = this.id || Roo.id();
108 // fill in the extra attributes
109 if (this.xattr && typeof(this.xattr) =='object') {
110 for (var i in this.xattr) {
111 cfg[i] = this.xattr[i];
116 cfg.dataId = this.dataId;
120 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
123 if (this.style) { // fixme needs to support more complex style data.
124 cfg.style = this.style;
128 cfg.name = this.name;
131 this.el = ct.createChild(cfg, position);
134 this.tooltipEl().attr('tooltip', this.tooltip);
137 if(this.tabIndex !== undefined){
138 this.el.dom.setAttribute('tabIndex', this.tabIndex);
145 * Fetch the element to add children to
146 * @return {Roo.Element} defaults to this.el
148 getChildContainer : function()
153 * Fetch the element to display the tooltip on.
154 * @return {Roo.Element} defaults to this.el
156 tooltipEl : function()
161 addxtype : function(tree,cntr)
165 cn = Roo.factory(tree);
166 //Roo.log(['addxtype', cn]);
168 cn.parentType = this.xtype; //??
169 cn.parentId = this.id;
171 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172 if (typeof(cn.container_method) == 'string') {
173 cntr = cn.container_method;
177 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
179 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
181 var build_from_html = Roo.XComponent.build_from_html;
183 var is_body = (tree.xtype == 'Body') ;
185 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
187 var self_cntr_el = Roo.get(this[cntr](false));
189 // do not try and build conditional elements
190 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
194 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196 return this.addxtypeChild(tree,cntr, is_body);
199 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
202 return this.addxtypeChild(Roo.apply({}, tree),cntr);
205 Roo.log('skipping render');
211 if (!build_from_html) {
215 // this i think handles overlaying multiple children of the same type
216 // with the sam eelement.. - which might be buggy..
218 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
224 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
228 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
235 addxtypeChild : function (tree, cntr, is_body)
237 Roo.debug && Roo.log('addxtypeChild:' + cntr);
239 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
242 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243 (typeof(tree['flexy:foreach']) != 'undefined');
247 skip_children = false;
248 // render the element if it's not BODY.
251 // if parent was disabled, then do not try and create the children..
252 if(!this[cntr](true)){
257 cn = Roo.factory(tree);
259 cn.parentType = this.xtype; //??
260 cn.parentId = this.id;
262 var build_from_html = Roo.XComponent.build_from_html;
265 // does the container contain child eleemnts with 'xtype' attributes.
266 // that match this xtype..
267 // note - when we render we create these as well..
268 // so we should check to see if body has xtype set.
269 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
271 var self_cntr_el = Roo.get(this[cntr](false));
272 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
274 //Roo.log(Roo.XComponent.build_from_html);
275 //Roo.log("got echild:");
278 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279 // and are not displayed -this causes this to use up the wrong element when matching.
280 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
283 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
290 //echild.dom.removeAttribute('xtype');
292 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293 Roo.debug && Roo.log(self_cntr_el);
294 Roo.debug && Roo.log(echild);
295 Roo.debug && Roo.log(cn);
301 // if object has flexy:if - then it may or may not be rendered.
302 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
303 // skip a flexy if element.
304 Roo.debug && Roo.log('skipping render');
305 Roo.debug && Roo.log(tree);
307 Roo.debug && Roo.log('skipping all children');
308 skip_children = true;
313 // actually if flexy:foreach is found, we really want to create
314 // multiple copies here...
316 //Roo.log(this[cntr]());
317 // some elements do not have render methods.. like the layouts...
319 if(this[cntr](true) === false){
324 cn.render && cn.render(this[cntr](true));
327 // then add the element..
334 if (typeof (tree.menu) != 'undefined') {
335 tree.menu.parentType = cn.xtype;
336 tree.menu.triggerEl = cn.el;
337 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
341 if (!tree.items || !tree.items.length) {
343 //Roo.log(["no children", this]);
348 var items = tree.items;
351 //Roo.log(items.length);
353 if (!skip_children) {
354 for(var i =0;i < items.length;i++) {
355 // Roo.log(['add child', items[i]]);
356 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
362 //Roo.log("fire childrenrendered");
364 cn.fireEvent('childrenrendered', this);
370 * Set the element that will be used to show or hide
372 setVisibilityEl : function(el)
374 this.visibilityEl = el;
378 * Get the element that will be used to show or hide
380 getVisibilityEl : function()
382 if (typeof(this.visibilityEl) == 'object') {
383 return this.visibilityEl;
386 if (typeof(this.visibilityEl) == 'string') {
387 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
394 * Show a component - removes 'hidden' class
398 if(!this.getVisibilityEl()){
402 this.getVisibilityEl().removeClass('hidden');
404 this.fireEvent('show', this);
409 * Hide a component - adds 'hidden' class
413 if(!this.getVisibilityEl()){
417 this.getVisibilityEl().addClass('hidden');
419 this.fireEvent('hide', this);
432 * @class Roo.bootstrap.Body
433 * @extends Roo.bootstrap.Component
434 * Bootstrap Body class
438 * @param {Object} config The config object
441 Roo.bootstrap.Body = function(config){
443 config = config || {};
445 Roo.bootstrap.Body.superclass.constructor.call(this, config);
446 this.el = Roo.get(config.el ? config.el : document.body );
447 if (this.cls && this.cls.length) {
448 Roo.get(document.body).addClass(this.cls);
452 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
454 is_body : true,// just to make sure it's constructed?
459 onRender : function(ct, position)
461 /* Roo.log("Roo.bootstrap.Body - onRender");
462 if (this.cls && this.cls.length) {
463 Roo.get(document.body).addClass(this.cls);
482 * @class Roo.bootstrap.ButtonGroup
483 * @extends Roo.bootstrap.Component
484 * Bootstrap ButtonGroup class
485 * @cfg {String} size lg | sm | xs (default empty normal)
486 * @cfg {String} align vertical | justified (default none)
487 * @cfg {String} direction up | down (default down)
488 * @cfg {Boolean} toolbar false | true
489 * @cfg {Boolean} btn true | false
494 * @param {Object} config The config object
497 Roo.bootstrap.ButtonGroup = function(config){
498 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
501 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
509 getAutoCreate : function(){
515 cfg.html = this.html || cfg.html;
526 if (['vertical','justified'].indexOf(this.align)!==-1) {
527 cfg.cls = 'btn-group-' + this.align;
529 if (this.align == 'justified') {
530 console.log(this.items);
534 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
535 cfg.cls += ' btn-group-' + this.size;
538 if (this.direction == 'up') {
539 cfg.cls += ' dropup' ;
555 * @class Roo.bootstrap.Button
556 * @extends Roo.bootstrap.Component
557 * Bootstrap Button class
558 * @cfg {String} html The button content
559 * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default
560 * @cfg {String} size ( lg | sm | xs)
561 * @cfg {String} tag ( a | input | submit)
562 * @cfg {String} href empty or href
563 * @cfg {Boolean} disabled default false;
564 * @cfg {Boolean} isClose default false;
565 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
566 * @cfg {String} badge text for badge
567 * @cfg {String} theme (default|glow)
568 * @cfg {Boolean} inverse dark themed version
569 * @cfg {Boolean} toggle is it a slidy toggle button
570 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
571 * @cfg {String} ontext text for on slidy toggle state
572 * @cfg {String} offtext text for off slidy toggle state
573 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
574 * @cfg {Boolean} removeClass remove the standard class..
575 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
578 * Create a new button
579 * @param {Object} config The config object
583 Roo.bootstrap.Button = function(config){
584 Roo.bootstrap.Button.superclass.constructor.call(this, config);
585 this.weightClass = ["btn-default",
597 * When a butotn is pressed
598 * @param {Roo.bootstrap.Button} btn
599 * @param {Roo.EventObject} e
604 * After the button has been toggles
605 * @param {Roo.bootstrap.Button} btn
606 * @param {Roo.EventObject} e
607 * @param {boolean} pressed (also available as button.pressed)
613 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
631 preventDefault: true,
639 getAutoCreate : function(){
647 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
648 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
653 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
655 if (this.toggle == true) {
658 cls: 'slider-frame roo-button',
663 'data-off-text':'OFF',
664 cls: 'slider-button',
670 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
671 cfg.cls += ' '+this.weight;
680 cfg["aria-hidden"] = true;
682 cfg.html = "×";
688 if (this.theme==='default') {
689 cfg.cls = 'btn roo-button';
691 //if (this.parentType != 'Navbar') {
692 this.weight = this.weight.length ? this.weight : 'default';
694 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
696 cfg.cls += ' btn-' + this.weight;
698 } else if (this.theme==='glow') {
701 cfg.cls = 'btn-glow roo-button';
703 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
705 cfg.cls += ' ' + this.weight;
711 this.cls += ' inverse';
715 if (this.active || this.pressed === true) {
716 cfg.cls += ' active';
720 cfg.disabled = 'disabled';
724 Roo.log('changing to ul' );
726 this.glyphicon = 'caret';
729 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
731 //gsRoo.log(this.parentType);
732 if (this.parentType === 'Navbar' && !this.parent().bar) {
733 Roo.log('changing to li?');
742 href : this.href || '#'
745 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
746 cfg.cls += ' dropdown';
753 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
755 if (this.glyphicon) {
756 cfg.html = ' ' + cfg.html;
761 cls: 'glyphicon glyphicon-' + this.glyphicon
771 // cfg.cls='btn roo-button';
775 var value = cfg.html;
780 cls: 'glyphicon glyphicon-' + this.glyphicon,
799 cfg.cls += ' dropdown';
800 cfg.html = typeof(cfg.html) != 'undefined' ?
801 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
804 if (cfg.tag !== 'a' && this.href !== '') {
805 throw "Tag must be a to set href.";
806 } else if (this.href.length > 0) {
807 cfg.href = this.href;
810 if(this.removeClass){
815 cfg.target = this.target;
820 initEvents: function() {
821 // Roo.log('init events?');
822 // Roo.log(this.el.dom);
825 if (typeof (this.menu) != 'undefined') {
826 this.menu.parentType = this.xtype;
827 this.menu.triggerEl = this.el;
828 this.addxtype(Roo.apply({}, this.menu));
832 if (this.el.hasClass('roo-button')) {
833 this.el.on('click', this.onClick, this);
835 this.el.select('.roo-button').on('click', this.onClick, this);
838 if(this.removeClass){
839 this.el.on('click', this.onClick, this);
842 this.el.enableDisplayMode();
845 onClick : function(e)
851 Roo.log('button on click ');
852 if(this.preventDefault){
856 if (this.pressed === true || this.pressed === false) {
857 this.toggleActive(e);
861 this.fireEvent('click', this, e);
865 * Enables this button
869 this.disabled = false;
870 this.el.removeClass('disabled');
874 * Disable this button
878 this.disabled = true;
879 this.el.addClass('disabled');
882 * sets the active state on/off,
883 * @param {Boolean} state (optional) Force a particular state
885 setActive : function(v) {
887 this.el[v ? 'addClass' : 'removeClass']('active');
891 * toggles the current active state
893 toggleActive : function(e)
895 this.setActive(!this.pressed);
896 this.fireEvent('toggle', this, e, !this.pressed);
899 * get the current active state
900 * @return {boolean} true if it's active
902 isActive : function()
904 return this.el.hasClass('active');
907 * set the text of the first selected button
909 setText : function(str)
911 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
914 * get the text of the first selected button
918 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
921 setWeight : function(str)
923 this.el.removeClass(this.weightClass);
924 this.el.addClass('btn-' + str);
938 * @class Roo.bootstrap.Column
939 * @extends Roo.bootstrap.Component
940 * Bootstrap Column class
941 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
942 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
943 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
944 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
945 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
946 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
947 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
948 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
951 * @cfg {Boolean} hidden (true|false) hide the element
952 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
953 * @cfg {String} fa (ban|check|...) font awesome icon
954 * @cfg {Number} fasize (1|2|....) font awsome size
956 * @cfg {String} icon (info-sign|check|...) glyphicon name
958 * @cfg {String} html content of column.
961 * Create a new Column
962 * @param {Object} config The config object
965 Roo.bootstrap.Column = function(config){
966 Roo.bootstrap.Column.superclass.constructor.call(this, config);
969 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
987 getAutoCreate : function(){
988 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
996 ['xs','sm','md','lg'].map(function(size){
997 //Roo.log( size + ':' + settings[size]);
999 if (settings[size+'off'] !== false) {
1000 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1003 if (settings[size] === false) {
1007 if (!settings[size]) { // 0 = hidden
1008 cfg.cls += ' hidden-' + size;
1011 cfg.cls += ' col-' + size + '-' + settings[size];
1016 cfg.cls += ' hidden';
1019 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1020 cfg.cls +=' alert alert-' + this.alert;
1024 if (this.html.length) {
1025 cfg.html = this.html;
1029 if (this.fasize > 1) {
1030 fasize = ' fa-' + this.fasize + 'x';
1032 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1037 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1056 * @class Roo.bootstrap.Container
1057 * @extends Roo.bootstrap.Component
1058 * Bootstrap Container class
1059 * @cfg {Boolean} jumbotron is it a jumbotron element
1060 * @cfg {String} html content of element
1061 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1062 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1063 * @cfg {String} header content of header (for panel)
1064 * @cfg {String} footer content of footer (for panel)
1065 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1066 * @cfg {String} tag (header|aside|section) type of HTML tag.
1067 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1068 * @cfg {String} fa font awesome icon
1069 * @cfg {String} icon (info-sign|check|...) glyphicon name
1070 * @cfg {Boolean} hidden (true|false) hide the element
1071 * @cfg {Boolean} expandable (true|false) default false
1072 * @cfg {Boolean} expanded (true|false) default true
1073 * @cfg {String} rheader contet on the right of header
1074 * @cfg {Boolean} clickable (true|false) default false
1078 * Create a new Container
1079 * @param {Object} config The config object
1082 Roo.bootstrap.Container = function(config){
1083 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1089 * After the panel has been expand
1091 * @param {Roo.bootstrap.Container} this
1096 * After the panel has been collapsed
1098 * @param {Roo.bootstrap.Container} this
1103 * When a element is chick
1104 * @param {Roo.bootstrap.Container} this
1105 * @param {Roo.EventObject} e
1111 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1129 getChildContainer : function() {
1135 if (this.panel.length) {
1136 return this.el.select('.panel-body',true).first();
1143 getAutoCreate : function(){
1146 tag : this.tag || 'div',
1150 if (this.jumbotron) {
1151 cfg.cls = 'jumbotron';
1156 // - this is applied by the parent..
1158 // cfg.cls = this.cls + '';
1161 if (this.sticky.length) {
1163 var bd = Roo.get(document.body);
1164 if (!bd.hasClass('bootstrap-sticky')) {
1165 bd.addClass('bootstrap-sticky');
1166 Roo.select('html',true).setStyle('height', '100%');
1169 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1173 if (this.well.length) {
1174 switch (this.well) {
1177 cfg.cls +=' well well-' +this.well;
1186 cfg.cls += ' hidden';
1190 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1191 cfg.cls +=' alert alert-' + this.alert;
1196 if (this.panel.length) {
1197 cfg.cls += ' panel panel-' + this.panel;
1199 if (this.header.length) {
1203 if(this.expandable){
1205 cfg.cls = cfg.cls + ' expandable';
1209 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1217 cls : 'panel-title',
1218 html : (this.expandable ? ' ' : '') + this.header
1222 cls: 'panel-header-right',
1228 cls : 'panel-heading',
1229 style : this.expandable ? 'cursor: pointer' : '',
1237 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1242 if (this.footer.length) {
1244 cls : 'panel-footer',
1253 body.html = this.html || cfg.html;
1254 // prefix with the icons..
1256 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1259 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1264 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1265 cfg.cls = 'container';
1271 initEvents: function()
1273 if(this.expandable){
1274 var headerEl = this.headerEl();
1277 headerEl.on('click', this.onToggleClick, this);
1282 this.el.on('click', this.onClick, this);
1287 onToggleClick : function()
1289 var headerEl = this.headerEl();
1305 if(this.fireEvent('expand', this)) {
1307 this.expanded = true;
1309 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1311 this.el.select('.panel-body',true).first().removeClass('hide');
1313 var toggleEl = this.toggleEl();
1319 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1324 collapse : function()
1326 if(this.fireEvent('collapse', this)) {
1328 this.expanded = false;
1330 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1331 this.el.select('.panel-body',true).first().addClass('hide');
1333 var toggleEl = this.toggleEl();
1339 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1343 toggleEl : function()
1345 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1349 return this.el.select('.panel-heading .fa',true).first();
1352 headerEl : function()
1354 if(!this.el || !this.panel.length || !this.header.length){
1358 return this.el.select('.panel-heading',true).first()
1363 if(!this.el || !this.panel.length){
1367 return this.el.select('.panel-body',true).first()
1370 titleEl : function()
1372 if(!this.el || !this.panel.length || !this.header.length){
1376 return this.el.select('.panel-title',true).first();
1379 setTitle : function(v)
1381 var titleEl = this.titleEl();
1387 titleEl.dom.innerHTML = v;
1390 getTitle : function()
1393 var titleEl = this.titleEl();
1399 return titleEl.dom.innerHTML;
1402 setRightTitle : function(v)
1404 var t = this.el.select('.panel-header-right',true).first();
1410 t.dom.innerHTML = v;
1413 onClick : function(e)
1417 this.fireEvent('click', this, e);
1430 * @class Roo.bootstrap.Img
1431 * @extends Roo.bootstrap.Component
1432 * Bootstrap Img class
1433 * @cfg {Boolean} imgResponsive false | true
1434 * @cfg {String} border rounded | circle | thumbnail
1435 * @cfg {String} src image source
1436 * @cfg {String} alt image alternative text
1437 * @cfg {String} href a tag href
1438 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1439 * @cfg {String} xsUrl xs image source
1440 * @cfg {String} smUrl sm image source
1441 * @cfg {String} mdUrl md image source
1442 * @cfg {String} lgUrl lg image source
1445 * Create a new Input
1446 * @param {Object} config The config object
1449 Roo.bootstrap.Img = function(config){
1450 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1456 * The img click event for the img.
1457 * @param {Roo.EventObject} e
1463 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1465 imgResponsive: true,
1475 getAutoCreate : function()
1477 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1478 return this.createSingleImg();
1483 cls: 'roo-image-responsive-group',
1488 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1490 if(!_this[size + 'Url']){
1496 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1497 html: _this.html || cfg.html,
1498 src: _this[size + 'Url']
1501 img.cls += ' roo-image-responsive-' + size;
1503 var s = ['xs', 'sm', 'md', 'lg'];
1505 s.splice(s.indexOf(size), 1);
1507 Roo.each(s, function(ss){
1508 img.cls += ' hidden-' + ss;
1511 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1512 cfg.cls += ' img-' + _this.border;
1516 cfg.alt = _this.alt;
1529 a.target = _this.target;
1533 cfg.cn.push((_this.href) ? a : img);
1540 createSingleImg : function()
1544 cls: (this.imgResponsive) ? 'img-responsive' : '',
1546 src : 'about:blank' // just incase src get's set to undefined?!?
1549 cfg.html = this.html || cfg.html;
1551 cfg.src = this.src || cfg.src;
1553 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1554 cfg.cls += ' img-' + this.border;
1571 a.target = this.target;
1576 return (this.href) ? a : cfg;
1579 initEvents: function()
1582 this.el.on('click', this.onClick, this);
1587 onClick : function(e)
1589 Roo.log('img onclick');
1590 this.fireEvent('click', this, e);
1593 * Sets the url of the image - used to update it
1594 * @param {String} url the url of the image
1597 setSrc : function(url)
1601 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1602 this.el.dom.src = url;
1606 this.el.select('img', true).first().dom.src = url;
1622 * @class Roo.bootstrap.Link
1623 * @extends Roo.bootstrap.Component
1624 * Bootstrap Link Class
1625 * @cfg {String} alt image alternative text
1626 * @cfg {String} href a tag href
1627 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1628 * @cfg {String} html the content of the link.
1629 * @cfg {String} anchor name for the anchor link
1630 * @cfg {String} fa - favicon
1632 * @cfg {Boolean} preventDefault (true | false) default false
1636 * Create a new Input
1637 * @param {Object} config The config object
1640 Roo.bootstrap.Link = function(config){
1641 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1647 * The img click event for the img.
1648 * @param {Roo.EventObject} e
1654 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1658 preventDefault: false,
1664 getAutoCreate : function()
1666 var html = this.html || '';
1668 if (this.fa !== false) {
1669 html = '<i class="fa fa-' + this.fa + '"></i>';
1674 // anchor's do not require html/href...
1675 if (this.anchor === false) {
1677 cfg.href = this.href || '#';
1679 cfg.name = this.anchor;
1680 if (this.html !== false || this.fa !== false) {
1683 if (this.href !== false) {
1684 cfg.href = this.href;
1688 if(this.alt !== false){
1693 if(this.target !== false) {
1694 cfg.target = this.target;
1700 initEvents: function() {
1702 if(!this.href || this.preventDefault){
1703 this.el.on('click', this.onClick, this);
1707 onClick : function(e)
1709 if(this.preventDefault){
1712 //Roo.log('img onclick');
1713 this.fireEvent('click', this, e);
1726 * @class Roo.bootstrap.Header
1727 * @extends Roo.bootstrap.Component
1728 * Bootstrap Header class
1729 * @cfg {String} html content of header
1730 * @cfg {Number} level (1|2|3|4|5|6) default 1
1733 * Create a new Header
1734 * @param {Object} config The config object
1738 Roo.bootstrap.Header = function(config){
1739 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1742 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1750 getAutoCreate : function(){
1755 tag: 'h' + (1 *this.level),
1756 html: this.html || ''
1768 * Ext JS Library 1.1.1
1769 * Copyright(c) 2006-2007, Ext JS, LLC.
1771 * Originally Released Under LGPL - original licence link has changed is not relivant.
1774 * <script type="text/javascript">
1778 * @class Roo.bootstrap.MenuMgr
1779 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1782 Roo.bootstrap.MenuMgr = function(){
1783 var menus, active, groups = {}, attached = false, lastShow = new Date();
1785 // private - called when first menu is created
1788 active = new Roo.util.MixedCollection();
1789 Roo.get(document).addKeyListener(27, function(){
1790 if(active.length > 0){
1798 if(active && active.length > 0){
1799 var c = active.clone();
1809 if(active.length < 1){
1810 Roo.get(document).un("mouseup", onMouseDown);
1818 var last = active.last();
1819 lastShow = new Date();
1822 Roo.get(document).on("mouseup", onMouseDown);
1827 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1828 m.parentMenu.activeChild = m;
1829 }else if(last && last.isVisible()){
1830 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1835 function onBeforeHide(m){
1837 m.activeChild.hide();
1839 if(m.autoHideTimer){
1840 clearTimeout(m.autoHideTimer);
1841 delete m.autoHideTimer;
1846 function onBeforeShow(m){
1847 var pm = m.parentMenu;
1848 if(!pm && !m.allowOtherMenus){
1850 }else if(pm && pm.activeChild && active != m){
1851 pm.activeChild.hide();
1855 // private this should really trigger on mouseup..
1856 function onMouseDown(e){
1857 Roo.log("on Mouse Up");
1859 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1860 Roo.log("MenuManager hideAll");
1869 function onBeforeCheck(mi, state){
1871 var g = groups[mi.group];
1872 for(var i = 0, l = g.length; i < l; i++){
1874 g[i].setChecked(false);
1883 * Hides all menus that are currently visible
1885 hideAll : function(){
1890 register : function(menu){
1894 menus[menu.id] = menu;
1895 menu.on("beforehide", onBeforeHide);
1896 menu.on("hide", onHide);
1897 menu.on("beforeshow", onBeforeShow);
1898 menu.on("show", onShow);
1900 if(g && menu.events["checkchange"]){
1904 groups[g].push(menu);
1905 menu.on("checkchange", onCheck);
1910 * Returns a {@link Roo.menu.Menu} object
1911 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1912 * be used to generate and return a new Menu instance.
1914 get : function(menu){
1915 if(typeof menu == "string"){ // menu id
1917 }else if(menu.events){ // menu instance
1920 /*else if(typeof menu.length == 'number'){ // array of menu items?
1921 return new Roo.bootstrap.Menu({items:menu});
1922 }else{ // otherwise, must be a config
1923 return new Roo.bootstrap.Menu(menu);
1930 unregister : function(menu){
1931 delete menus[menu.id];
1932 menu.un("beforehide", onBeforeHide);
1933 menu.un("hide", onHide);
1934 menu.un("beforeshow", onBeforeShow);
1935 menu.un("show", onShow);
1937 if(g && menu.events["checkchange"]){
1938 groups[g].remove(menu);
1939 menu.un("checkchange", onCheck);
1944 registerCheckable : function(menuItem){
1945 var g = menuItem.group;
1950 groups[g].push(menuItem);
1951 menuItem.on("beforecheckchange", onBeforeCheck);
1956 unregisterCheckable : function(menuItem){
1957 var g = menuItem.group;
1959 groups[g].remove(menuItem);
1960 menuItem.un("beforecheckchange", onBeforeCheck);
1972 * @class Roo.bootstrap.Menu
1973 * @extends Roo.bootstrap.Component
1974 * Bootstrap Menu class - container for MenuItems
1975 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1976 * @cfg {bool} hidden if the menu should be hidden when rendered.
1977 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1978 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1982 * @param {Object} config The config object
1986 Roo.bootstrap.Menu = function(config){
1987 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1988 if (this.registerMenu && this.type != 'treeview') {
1989 Roo.bootstrap.MenuMgr.register(this);
1994 * Fires before this menu is displayed
1995 * @param {Roo.menu.Menu} this
2000 * Fires before this menu is hidden
2001 * @param {Roo.menu.Menu} this
2006 * Fires after this menu is displayed
2007 * @param {Roo.menu.Menu} this
2012 * Fires after this menu is hidden
2013 * @param {Roo.menu.Menu} this
2018 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2019 * @param {Roo.menu.Menu} this
2020 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2021 * @param {Roo.EventObject} e
2026 * Fires when the mouse is hovering over this menu
2027 * @param {Roo.menu.Menu} this
2028 * @param {Roo.EventObject} e
2029 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2034 * Fires when the mouse exits this menu
2035 * @param {Roo.menu.Menu} this
2036 * @param {Roo.EventObject} e
2037 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2042 * Fires when a menu item contained in this menu is clicked
2043 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2044 * @param {Roo.EventObject} e
2048 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2051 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2055 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2058 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2060 registerMenu : true,
2062 menuItems :false, // stores the menu items..
2072 getChildContainer : function() {
2076 getAutoCreate : function(){
2078 //if (['right'].indexOf(this.align)!==-1) {
2079 // cfg.cn[1].cls += ' pull-right'
2085 cls : 'dropdown-menu' ,
2086 style : 'z-index:1000'
2090 if (this.type === 'submenu') {
2091 cfg.cls = 'submenu active';
2093 if (this.type === 'treeview') {
2094 cfg.cls = 'treeview-menu';
2099 initEvents : function() {
2101 // Roo.log("ADD event");
2102 // Roo.log(this.triggerEl.dom);
2104 this.triggerEl.on('click', this.onTriggerClick, this);
2106 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2108 this.triggerEl.addClass('dropdown-toggle');
2111 this.el.on('touchstart' , this.onTouch, this);
2113 this.el.on('click' , this.onClick, this);
2115 this.el.on("mouseover", this.onMouseOver, this);
2116 this.el.on("mouseout", this.onMouseOut, this);
2120 findTargetItem : function(e)
2122 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2126 //Roo.log(t); Roo.log(t.id);
2128 //Roo.log(this.menuitems);
2129 return this.menuitems.get(t.id);
2131 //return this.items.get(t.menuItemId);
2137 onTouch : function(e)
2139 Roo.log("menu.onTouch");
2140 //e.stopEvent(); this make the user popdown broken
2144 onClick : function(e)
2146 Roo.log("menu.onClick");
2148 var t = this.findTargetItem(e);
2149 if(!t || t.isContainer){
2154 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2155 if(t == this.activeItem && t.shouldDeactivate(e)){
2156 this.activeItem.deactivate();
2157 delete this.activeItem;
2161 this.setActiveItem(t, true);
2169 Roo.log('pass click event');
2173 this.fireEvent("click", this, t, e);
2177 if(!t.href.length || t.href == '#'){
2178 (function() { _this.hide(); }).defer(100);
2183 onMouseOver : function(e){
2184 var t = this.findTargetItem(e);
2187 // if(t.canActivate && !t.disabled){
2188 // this.setActiveItem(t, true);
2192 this.fireEvent("mouseover", this, e, t);
2194 isVisible : function(){
2195 return !this.hidden;
2197 onMouseOut : function(e){
2198 var t = this.findTargetItem(e);
2201 // if(t == this.activeItem && t.shouldDeactivate(e)){
2202 // this.activeItem.deactivate();
2203 // delete this.activeItem;
2206 this.fireEvent("mouseout", this, e, t);
2211 * Displays this menu relative to another element
2212 * @param {String/HTMLElement/Roo.Element} element The element to align to
2213 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2214 * the element (defaults to this.defaultAlign)
2215 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2217 show : function(el, pos, parentMenu){
2218 this.parentMenu = parentMenu;
2222 this.fireEvent("beforeshow", this);
2223 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2226 * Displays this menu at a specific xy position
2227 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2228 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2230 showAt : function(xy, parentMenu, /* private: */_e){
2231 this.parentMenu = parentMenu;
2236 this.fireEvent("beforeshow", this);
2237 //xy = this.el.adjustForConstraints(xy);
2241 this.hideMenuItems();
2242 this.hidden = false;
2243 this.triggerEl.addClass('open');
2245 // reassign x when hitting right
2246 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2247 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2250 // reassign y when hitting bottom
2251 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2252 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2255 // but the list may align on trigger left or trigger top... should it be a properity?
2257 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2262 this.fireEvent("show", this);
2268 this.doFocus.defer(50, this);
2272 doFocus : function(){
2274 this.focusEl.focus();
2279 * Hides this menu and optionally all parent menus
2280 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2282 hide : function(deep)
2285 this.hideMenuItems();
2286 if(this.el && this.isVisible()){
2287 this.fireEvent("beforehide", this);
2288 if(this.activeItem){
2289 this.activeItem.deactivate();
2290 this.activeItem = null;
2292 this.triggerEl.removeClass('open');;
2294 this.fireEvent("hide", this);
2296 if(deep === true && this.parentMenu){
2297 this.parentMenu.hide(true);
2301 onTriggerClick : function(e)
2303 Roo.log('trigger click');
2305 var target = e.getTarget();
2307 Roo.log(target.nodeName.toLowerCase());
2309 if(target.nodeName.toLowerCase() === 'i'){
2315 onTriggerPress : function(e)
2317 Roo.log('trigger press');
2318 //Roo.log(e.getTarget());
2319 // Roo.log(this.triggerEl.dom);
2321 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2322 var pel = Roo.get(e.getTarget());
2323 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2324 Roo.log('is treeview or dropdown?');
2328 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2332 if (this.isVisible()) {
2337 this.show(this.triggerEl, false, false);
2340 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2347 hideMenuItems : function()
2349 Roo.log("hide Menu Items");
2353 //$(backdrop).remove()
2354 this.el.select('.open',true).each(function(aa) {
2356 aa.removeClass('open');
2357 //var parent = getParent($(this))
2358 //var relatedTarget = { relatedTarget: this }
2360 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2361 //if (e.isDefaultPrevented()) return
2362 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2365 addxtypeChild : function (tree, cntr) {
2366 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2368 this.menuitems.add(comp);
2380 this.getEl().dom.innerHTML = '';
2381 this.menuitems.clear();
2395 * @class Roo.bootstrap.MenuItem
2396 * @extends Roo.bootstrap.Component
2397 * Bootstrap MenuItem class
2398 * @cfg {String} html the menu label
2399 * @cfg {String} href the link
2400 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2401 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2402 * @cfg {Boolean} active used on sidebars to highlight active itesm
2403 * @cfg {String} fa favicon to show on left of menu item.
2404 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2408 * Create a new MenuItem
2409 * @param {Object} config The config object
2413 Roo.bootstrap.MenuItem = function(config){
2414 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2419 * The raw click event for the entire grid.
2420 * @param {Roo.bootstrap.MenuItem} this
2421 * @param {Roo.EventObject} e
2427 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2431 preventDefault: false,
2432 isContainer : false,
2436 getAutoCreate : function(){
2438 if(this.isContainer){
2441 cls: 'dropdown-menu-item'
2455 if (this.fa !== false) {
2458 cls : 'fa fa-' + this.fa
2467 cls: 'dropdown-menu-item',
2470 if (this.parent().type == 'treeview') {
2471 cfg.cls = 'treeview-menu';
2474 cfg.cls += ' active';
2479 anc.href = this.href || cfg.cn[0].href ;
2480 ctag.html = this.html || cfg.cn[0].html ;
2484 initEvents: function()
2486 if (this.parent().type == 'treeview') {
2487 this.el.select('a').on('click', this.onClick, this);
2491 this.menu.parentType = this.xtype;
2492 this.menu.triggerEl = this.el;
2493 this.menu = this.addxtype(Roo.apply({}, this.menu));
2497 onClick : function(e)
2499 Roo.log('item on click ');
2501 if(this.preventDefault){
2504 //this.parent().hideMenuItems();
2506 this.fireEvent('click', this, e);
2525 * @class Roo.bootstrap.MenuSeparator
2526 * @extends Roo.bootstrap.Component
2527 * Bootstrap MenuSeparator class
2530 * Create a new MenuItem
2531 * @param {Object} config The config object
2535 Roo.bootstrap.MenuSeparator = function(config){
2536 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2539 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2541 getAutoCreate : function(){
2560 * @class Roo.bootstrap.Modal
2561 * @extends Roo.bootstrap.Component
2562 * Bootstrap Modal class
2563 * @cfg {String} title Title of dialog
2564 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2565 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2566 * @cfg {Boolean} specificTitle default false
2567 * @cfg {Array} buttons Array of buttons or standard button set..
2568 * @cfg {String} buttonPosition (left|right|center) default right
2569 * @cfg {Boolean} animate default true
2570 * @cfg {Boolean} allow_close default true
2571 * @cfg {Boolean} fitwindow default false
2572 * @cfg {String} size (sm|lg) default empty
2573 * @cfg {Number} max_width set the max width of modal
2577 * Create a new Modal Dialog
2578 * @param {Object} config The config object
2581 Roo.bootstrap.Modal = function(config){
2582 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2587 * The raw btnclick event for the button
2588 * @param {Roo.EventObject} e
2593 * Fire when dialog resize
2594 * @param {Roo.bootstrap.Modal} this
2595 * @param {Roo.EventObject} e
2599 this.buttons = this.buttons || [];
2602 this.tmpl = Roo.factory(this.tmpl);
2607 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2609 title : 'test dialog',
2619 specificTitle: false,
2621 buttonPosition: 'right',
2644 onRender : function(ct, position)
2646 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2649 var cfg = Roo.apply({}, this.getAutoCreate());
2652 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2654 //if (!cfg.name.length) {
2658 cfg.cls += ' ' + this.cls;
2661 cfg.style = this.style;
2663 this.el = Roo.get(document.body).createChild(cfg, position);
2665 //var type = this.el.dom.type;
2668 if(this.tabIndex !== undefined){
2669 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2672 this.dialogEl = this.el.select('.modal-dialog',true).first();
2673 this.bodyEl = this.el.select('.modal-body',true).first();
2674 this.closeEl = this.el.select('.modal-header .close', true).first();
2675 this.headerEl = this.el.select('.modal-header',true).first();
2676 this.titleEl = this.el.select('.modal-title',true).first();
2677 this.footerEl = this.el.select('.modal-footer',true).first();
2679 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2681 //this.el.addClass("x-dlg-modal");
2683 if (this.buttons.length) {
2684 Roo.each(this.buttons, function(bb) {
2685 var b = Roo.apply({}, bb);
2686 b.xns = b.xns || Roo.bootstrap;
2687 b.xtype = b.xtype || 'Button';
2688 if (typeof(b.listeners) == 'undefined') {
2689 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2692 var btn = Roo.factory(b);
2694 btn.render(this.el.select('.modal-footer div').first());
2698 // render the children.
2701 if(typeof(this.items) != 'undefined'){
2702 var items = this.items;
2705 for(var i =0;i < items.length;i++) {
2706 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2710 this.items = nitems;
2712 // where are these used - they used to be body/close/footer
2716 //this.el.addClass([this.fieldClass, this.cls]);
2720 getAutoCreate : function()
2724 html : this.html || ''
2729 cls : 'modal-title',
2733 if(this.specificTitle){
2739 if (this.allow_close) {
2751 if(this.size.length){
2752 size = 'modal-' + this.size;
2759 cls: "modal-dialog " + size,
2762 cls : "modal-content",
2765 cls : 'modal-header',
2770 cls : 'modal-footer',
2774 cls: 'btn-' + this.buttonPosition
2791 modal.cls += ' fade';
2797 getChildContainer : function() {
2802 getButtonContainer : function() {
2803 return this.el.select('.modal-footer div',true).first();
2806 initEvents : function()
2808 if (this.allow_close) {
2809 this.closeEl.on('click', this.hide, this);
2811 Roo.EventManager.onWindowResize(this.resize, this, true);
2818 this.maskEl.setSize(
2819 Roo.lib.Dom.getViewWidth(true),
2820 Roo.lib.Dom.getViewHeight(true)
2823 if (this.fitwindow) {
2825 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2826 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2831 if(this.max_width !== 0) {
2833 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2836 this.setSize(w, this.height);
2840 if(this.max_height) {
2841 this.setSize(w,Math.min(
2843 Roo.lib.Dom.getViewportHeight(true) - 60
2849 if(!this.fit_content) {
2850 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2854 this.setSize(w, Math.min(
2856 this.headerEl.getHeight() +
2857 this.footerEl.getHeight() +
2858 this.getChildHeight(this.bodyEl.dom.childNodes),
2859 Roo.lib.Dom.getViewportHeight(true) - 60)
2865 setSize : function(w,h)
2876 if (!this.rendered) {
2880 //this.el.setStyle('display', 'block');
2881 this.el.removeClass('hideing');
2882 this.el.addClass('show');
2884 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2887 this.el.addClass('in');
2890 this.el.addClass('in');
2893 // not sure how we can show data in here..
2895 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2898 Roo.get(document.body).addClass("x-body-masked");
2900 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2901 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2902 this.maskEl.addClass('show');
2906 this.fireEvent('show', this);
2908 // set zindex here - otherwise it appears to be ignored...
2909 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2912 this.items.forEach( function(e) {
2913 e.layout ? e.layout() : false;
2921 if(this.fireEvent("beforehide", this) !== false){
2922 this.maskEl.removeClass('show');
2923 Roo.get(document.body).removeClass("x-body-masked");
2924 this.el.removeClass('in');
2925 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2927 if(this.animate){ // why
2928 this.el.addClass('hideing');
2930 if (!this.el.hasClass('hideing')) {
2931 return; // it's been shown again...
2933 this.el.removeClass('show');
2934 this.el.removeClass('hideing');
2938 this.el.removeClass('show');
2940 this.fireEvent('hide', this);
2943 isVisible : function()
2946 return this.el.hasClass('show') && !this.el.hasClass('hideing');
2950 addButton : function(str, cb)
2954 var b = Roo.apply({}, { html : str } );
2955 b.xns = b.xns || Roo.bootstrap;
2956 b.xtype = b.xtype || 'Button';
2957 if (typeof(b.listeners) == 'undefined') {
2958 b.listeners = { click : cb.createDelegate(this) };
2961 var btn = Roo.factory(b);
2963 btn.render(this.el.select('.modal-footer div').first());
2969 setDefaultButton : function(btn)
2971 //this.el.select('.modal-footer').()
2975 resizeTo: function(w,h)
2979 this.dialogEl.setWidth(w);
2980 if (this.diff === false) {
2981 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2984 this.bodyEl.setHeight(h - this.diff);
2986 this.fireEvent('resize', this);
2989 setContentSize : function(w, h)
2993 onButtonClick: function(btn,e)
2996 this.fireEvent('btnclick', btn.name, e);
2999 * Set the title of the Dialog
3000 * @param {String} str new Title
3002 setTitle: function(str) {
3003 this.titleEl.dom.innerHTML = str;
3006 * Set the body of the Dialog
3007 * @param {String} str new Title
3009 setBody: function(str) {
3010 this.bodyEl.dom.innerHTML = str;
3013 * Set the body of the Dialog using the template
3014 * @param {Obj} data - apply this data to the template and replace the body contents.
3016 applyBody: function(obj)
3019 Roo.log("Error - using apply Body without a template");
3022 this.tmpl.overwrite(this.bodyEl, obj);
3025 getChildHeight : function(child_nodes)
3029 child_nodes.length == 0
3034 var child_height = 0;
3036 for(var i = 0; i < child_nodes.length; i++) {
3039 * for modal with tabs...
3040 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3042 var layout_childs = child_nodes[i].childNodes;
3044 for(var j = 0; j < layout_childs.length; j++) {
3046 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3048 var layout_body_childs = layout_childs[j].childNodes;
3050 for(var k = 0; k < layout_body_childs.length; k++) {
3052 if(layout_body_childs[k].classList.contains('navbar')) {
3053 child_height += layout_body_childs[k].offsetHeight;
3057 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3059 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3061 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3063 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3064 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3079 child_height += child_nodes[i].offsetHeight;
3080 // Roo.log(child_nodes[i].offsetHeight);
3083 return child_height;
3089 Roo.apply(Roo.bootstrap.Modal, {
3091 * Button config that displays a single OK button
3100 * Button config that displays Yes and No buttons
3116 * Button config that displays OK and Cancel buttons
3131 * Button config that displays Yes, No and Cancel buttons
3155 * messagebox - can be used as a replace
3159 * @class Roo.MessageBox
3160 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3164 Roo.Msg.alert('Status', 'Changes saved successfully.');
3166 // Prompt for user data:
3167 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3169 // process text value...
3173 // Show a dialog using config options:
3175 title:'Save Changes?',
3176 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3177 buttons: Roo.Msg.YESNOCANCEL,
3184 Roo.bootstrap.MessageBox = function(){
3185 var dlg, opt, mask, waitTimer;
3186 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3187 var buttons, activeTextEl, bwidth;
3191 var handleButton = function(button){
3193 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3197 var handleHide = function(){
3199 dlg.el.removeClass(opt.cls);
3202 // Roo.TaskMgr.stop(waitTimer);
3203 // waitTimer = null;
3208 var updateButtons = function(b){
3211 buttons["ok"].hide();
3212 buttons["cancel"].hide();
3213 buttons["yes"].hide();
3214 buttons["no"].hide();
3215 //dlg.footer.dom.style.display = 'none';
3218 dlg.footerEl.dom.style.display = '';
3219 for(var k in buttons){
3220 if(typeof buttons[k] != "function"){
3223 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3224 width += buttons[k].el.getWidth()+15;
3234 var handleEsc = function(d, k, e){
3235 if(opt && opt.closable !== false){
3245 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3246 * @return {Roo.BasicDialog} The BasicDialog element
3248 getDialog : function(){
3250 dlg = new Roo.bootstrap.Modal( {
3253 //constraintoviewport:false,
3255 //collapsible : false,
3260 //buttonAlign:"center",
3261 closeClick : function(){
3262 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3265 handleButton("cancel");
3270 dlg.on("hide", handleHide);
3272 //dlg.addKeyListener(27, handleEsc);
3274 this.buttons = buttons;
3275 var bt = this.buttonText;
3276 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3277 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3278 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3279 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3281 bodyEl = dlg.bodyEl.createChild({
3283 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3284 '<textarea class="roo-mb-textarea"></textarea>' +
3285 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3287 msgEl = bodyEl.dom.firstChild;
3288 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3289 textboxEl.enableDisplayMode();
3290 textboxEl.addKeyListener([10,13], function(){
3291 if(dlg.isVisible() && opt && opt.buttons){
3294 }else if(opt.buttons.yes){
3295 handleButton("yes");
3299 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3300 textareaEl.enableDisplayMode();
3301 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3302 progressEl.enableDisplayMode();
3304 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3305 var pf = progressEl.dom.firstChild;
3307 pp = Roo.get(pf.firstChild);
3308 pp.setHeight(pf.offsetHeight);
3316 * Updates the message box body text
3317 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3318 * the XHTML-compliant non-breaking space character '&#160;')
3319 * @return {Roo.MessageBox} This message box
3321 updateText : function(text)
3323 if(!dlg.isVisible() && !opt.width){
3324 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3325 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3327 msgEl.innerHTML = text || ' ';
3329 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3330 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3332 Math.min(opt.width || cw , this.maxWidth),
3333 Math.max(opt.minWidth || this.minWidth, bwidth)
3336 activeTextEl.setWidth(w);
3338 if(dlg.isVisible()){
3339 dlg.fixedcenter = false;
3341 // to big, make it scroll. = But as usual stupid IE does not support
3344 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3345 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3346 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3348 bodyEl.dom.style.height = '';
3349 bodyEl.dom.style.overflowY = '';
3352 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3354 bodyEl.dom.style.overflowX = '';
3357 dlg.setContentSize(w, bodyEl.getHeight());
3358 if(dlg.isVisible()){
3359 dlg.fixedcenter = true;
3365 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3366 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3367 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3368 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3369 * @return {Roo.MessageBox} This message box
3371 updateProgress : function(value, text){
3373 this.updateText(text);
3376 if (pp) { // weird bug on my firefox - for some reason this is not defined
3377 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3378 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3384 * Returns true if the message box is currently displayed
3385 * @return {Boolean} True if the message box is visible, else false
3387 isVisible : function(){
3388 return dlg && dlg.isVisible();
3392 * Hides the message box if it is displayed
3395 if(this.isVisible()){
3401 * Displays a new message box, or reinitializes an existing message box, based on the config options
3402 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3403 * The following config object properties are supported:
3405 Property Type Description
3406 ---------- --------------- ------------------------------------------------------------------------------------
3407 animEl String/Element An id or Element from which the message box should animate as it opens and
3408 closes (defaults to undefined)
3409 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3410 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3411 closable Boolean False to hide the top-right close button (defaults to true). Note that
3412 progress and wait dialogs will ignore this property and always hide the
3413 close button as they can only be closed programmatically.
3414 cls String A custom CSS class to apply to the message box element
3415 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3416 displayed (defaults to 75)
3417 fn Function A callback function to execute after closing the dialog. The arguments to the
3418 function will be btn (the name of the button that was clicked, if applicable,
3419 e.g. "ok"), and text (the value of the active text field, if applicable).
3420 Progress and wait dialogs will ignore this option since they do not respond to
3421 user actions and can only be closed programmatically, so any required function
3422 should be called by the same code after it closes the dialog.
3423 icon String A CSS class that provides a background image to be used as an icon for
3424 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3425 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3426 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3427 modal Boolean False to allow user interaction with the page while the message box is
3428 displayed (defaults to true)
3429 msg String A string that will replace the existing message box body text (defaults
3430 to the XHTML-compliant non-breaking space character ' ')
3431 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3432 progress Boolean True to display a progress bar (defaults to false)
3433 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3434 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3435 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3436 title String The title text
3437 value String The string value to set into the active textbox element if displayed
3438 wait Boolean True to display a progress bar (defaults to false)
3439 width Number The width of the dialog in pixels
3446 msg: 'Please enter your address:',
3448 buttons: Roo.MessageBox.OKCANCEL,
3451 animEl: 'addAddressBtn'
3454 * @param {Object} config Configuration options
3455 * @return {Roo.MessageBox} This message box
3457 show : function(options)
3460 // this causes nightmares if you show one dialog after another
3461 // especially on callbacks..
3463 if(this.isVisible()){
3466 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3467 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3468 Roo.log("New Dialog Message:" + options.msg )
3469 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3470 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3473 var d = this.getDialog();
3475 d.setTitle(opt.title || " ");
3476 d.closeEl.setDisplayed(opt.closable !== false);
3477 activeTextEl = textboxEl;
3478 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3483 textareaEl.setHeight(typeof opt.multiline == "number" ?
3484 opt.multiline : this.defaultTextHeight);
3485 activeTextEl = textareaEl;
3494 progressEl.setDisplayed(opt.progress === true);
3495 this.updateProgress(0);
3496 activeTextEl.dom.value = opt.value || "";
3498 dlg.setDefaultButton(activeTextEl);
3500 var bs = opt.buttons;
3504 }else if(bs && bs.yes){
3505 db = buttons["yes"];
3507 dlg.setDefaultButton(db);
3509 bwidth = updateButtons(opt.buttons);
3510 this.updateText(opt.msg);
3512 d.el.addClass(opt.cls);
3514 d.proxyDrag = opt.proxyDrag === true;
3515 d.modal = opt.modal !== false;
3516 d.mask = opt.modal !== false ? mask : false;
3518 // force it to the end of the z-index stack so it gets a cursor in FF
3519 document.body.appendChild(dlg.el.dom);
3520 d.animateTarget = null;
3521 d.show(options.animEl);
3527 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3528 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3529 * and closing the message box when the process is complete.
3530 * @param {String} title The title bar text
3531 * @param {String} msg The message box body text
3532 * @return {Roo.MessageBox} This message box
3534 progress : function(title, msg){
3541 minWidth: this.minProgressWidth,
3548 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3549 * If a callback function is passed it will be called after the user clicks the button, and the
3550 * id of the button that was clicked will be passed as the only parameter to the callback
3551 * (could also be the top-right close button).
3552 * @param {String} title The title bar text
3553 * @param {String} msg The message box body text
3554 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3555 * @param {Object} scope (optional) The scope of the callback function
3556 * @return {Roo.MessageBox} This message box
3558 alert : function(title, msg, fn, scope)
3573 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3574 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3575 * You are responsible for closing the message box when the process is complete.
3576 * @param {String} msg The message box body text
3577 * @param {String} title (optional) The title bar text
3578 * @return {Roo.MessageBox} This message box
3580 wait : function(msg, title){
3591 waitTimer = Roo.TaskMgr.start({
3593 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3601 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3602 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3603 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3604 * @param {String} title The title bar text
3605 * @param {String} msg The message box body text
3606 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3607 * @param {Object} scope (optional) The scope of the callback function
3608 * @return {Roo.MessageBox} This message box
3610 confirm : function(title, msg, fn, scope){
3614 buttons: this.YESNO,
3623 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3624 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3625 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3626 * (could also be the top-right close button) and the text that was entered will be passed as the two
3627 * parameters to the callback.
3628 * @param {String} title The title bar text
3629 * @param {String} msg The message box body text
3630 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3631 * @param {Object} scope (optional) The scope of the callback function
3632 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3633 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3634 * @return {Roo.MessageBox} This message box
3636 prompt : function(title, msg, fn, scope, multiline){
3640 buttons: this.OKCANCEL,
3645 multiline: multiline,
3652 * Button config that displays a single OK button
3657 * Button config that displays Yes and No buttons
3660 YESNO : {yes:true, no:true},
3662 * Button config that displays OK and Cancel buttons
3665 OKCANCEL : {ok:true, cancel:true},
3667 * Button config that displays Yes, No and Cancel buttons
3670 YESNOCANCEL : {yes:true, no:true, cancel:true},
3673 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3676 defaultTextHeight : 75,
3678 * The maximum width in pixels of the message box (defaults to 600)
3683 * The minimum width in pixels of the message box (defaults to 100)
3688 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3689 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3692 minProgressWidth : 250,
3694 * An object containing the default button text strings that can be overriden for localized language support.
3695 * Supported properties are: ok, cancel, yes and no.
3696 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3709 * Shorthand for {@link Roo.MessageBox}
3711 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3712 Roo.Msg = Roo.Msg || Roo.MessageBox;
3721 * @class Roo.bootstrap.Navbar
3722 * @extends Roo.bootstrap.Component
3723 * Bootstrap Navbar class
3726 * Create a new Navbar
3727 * @param {Object} config The config object
3731 Roo.bootstrap.Navbar = function(config){
3732 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3736 * @event beforetoggle
3737 * Fire before toggle the menu
3738 * @param {Roo.EventObject} e
3740 "beforetoggle" : true
3744 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3753 getAutoCreate : function(){
3756 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3760 initEvents :function ()
3762 //Roo.log(this.el.select('.navbar-toggle',true));
3763 this.el.select('.navbar-toggle',true).on('click', function() {
3764 if(this.fireEvent('beforetoggle', this) !== false){
3765 this.el.select('.navbar-collapse',true).toggleClass('in');
3775 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3777 var size = this.el.getSize();
3778 this.maskEl.setSize(size.width, size.height);
3779 this.maskEl.enableDisplayMode("block");
3788 getChildContainer : function()
3790 if (this.el.select('.collapse').getCount()) {
3791 return this.el.select('.collapse',true).first();
3824 * @class Roo.bootstrap.NavSimplebar
3825 * @extends Roo.bootstrap.Navbar
3826 * Bootstrap Sidebar class
3828 * @cfg {Boolean} inverse is inverted color
3830 * @cfg {String} type (nav | pills | tabs)
3831 * @cfg {Boolean} arrangement stacked | justified
3832 * @cfg {String} align (left | right) alignment
3834 * @cfg {Boolean} main (true|false) main nav bar? default false
3835 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3837 * @cfg {String} tag (header|footer|nav|div) default is nav
3843 * Create a new Sidebar
3844 * @param {Object} config The config object
3848 Roo.bootstrap.NavSimplebar = function(config){
3849 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3852 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3868 getAutoCreate : function(){
3872 tag : this.tag || 'div',
3885 this.type = this.type || 'nav';
3886 if (['tabs','pills'].indexOf(this.type)!==-1) {
3887 cfg.cn[0].cls += ' nav-' + this.type
3891 if (this.type!=='nav') {
3892 Roo.log('nav type must be nav/tabs/pills')
3894 cfg.cn[0].cls += ' navbar-nav'
3900 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3901 cfg.cn[0].cls += ' nav-' + this.arrangement;
3905 if (this.align === 'right') {
3906 cfg.cn[0].cls += ' navbar-right';
3910 cfg.cls += ' navbar-inverse';
3937 * @class Roo.bootstrap.NavHeaderbar
3938 * @extends Roo.bootstrap.NavSimplebar
3939 * Bootstrap Sidebar class
3941 * @cfg {String} brand what is brand
3942 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3943 * @cfg {String} brand_href href of the brand
3944 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3945 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3946 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3947 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3950 * Create a new Sidebar
3951 * @param {Object} config The config object
3955 Roo.bootstrap.NavHeaderbar = function(config){
3956 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3960 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3967 desktopCenter : false,
3970 getAutoCreate : function(){
3973 tag: this.nav || 'nav',
3980 if (this.desktopCenter) {
3981 cn.push({cls : 'container', cn : []});
3988 cls: 'navbar-header',
3993 cls: 'navbar-toggle',
3994 'data-toggle': 'collapse',
3999 html: 'Toggle navigation'
4021 cls: 'collapse navbar-collapse',
4025 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
4027 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4028 cfg.cls += ' navbar-' + this.position;
4030 // tag can override this..
4032 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4035 if (this.brand !== '') {
4038 href: this.brand_href ? this.brand_href : '#',
4039 cls: 'navbar-brand',
4047 cfg.cls += ' main-nav';
4055 getHeaderChildContainer : function()
4057 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4058 return this.el.select('.navbar-header',true).first();
4061 return this.getChildContainer();
4065 initEvents : function()
4067 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4069 if (this.autohide) {
4074 Roo.get(document).on('scroll',function(e) {
4075 var ns = Roo.get(document).getScroll().top;
4076 var os = prevScroll;
4080 ft.removeClass('slideDown');
4081 ft.addClass('slideUp');
4084 ft.removeClass('slideUp');
4085 ft.addClass('slideDown');
4106 * @class Roo.bootstrap.NavSidebar
4107 * @extends Roo.bootstrap.Navbar
4108 * Bootstrap Sidebar class
4111 * Create a new Sidebar
4112 * @param {Object} config The config object
4116 Roo.bootstrap.NavSidebar = function(config){
4117 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4120 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4122 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4124 getAutoCreate : function(){
4129 cls: 'sidebar sidebar-nav'
4151 * @class Roo.bootstrap.NavGroup
4152 * @extends Roo.bootstrap.Component
4153 * Bootstrap NavGroup class
4154 * @cfg {String} align (left|right)
4155 * @cfg {Boolean} inverse
4156 * @cfg {String} type (nav|pills|tab) default nav
4157 * @cfg {String} navId - reference Id for navbar.
4161 * Create a new nav group
4162 * @param {Object} config The config object
4165 Roo.bootstrap.NavGroup = function(config){
4166 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4169 Roo.bootstrap.NavGroup.register(this);
4173 * Fires when the active item changes
4174 * @param {Roo.bootstrap.NavGroup} this
4175 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4176 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4183 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4194 getAutoCreate : function()
4196 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4203 if (['tabs','pills'].indexOf(this.type)!==-1) {
4204 cfg.cls += ' nav-' + this.type
4206 if (this.type!=='nav') {
4207 Roo.log('nav type must be nav/tabs/pills')
4209 cfg.cls += ' navbar-nav'
4212 if (this.parent() && this.parent().sidebar) {
4215 cls: 'dashboard-menu sidebar-menu'
4221 if (this.form === true) {
4227 if (this.align === 'right') {
4228 cfg.cls += ' navbar-right';
4230 cfg.cls += ' navbar-left';
4234 if (this.align === 'right') {
4235 cfg.cls += ' navbar-right';
4239 cfg.cls += ' navbar-inverse';
4247 * sets the active Navigation item
4248 * @param {Roo.bootstrap.NavItem} the new current navitem
4250 setActiveItem : function(item)
4253 Roo.each(this.navItems, function(v){
4258 v.setActive(false, true);
4265 item.setActive(true, true);
4266 this.fireEvent('changed', this, item, prev);
4271 * gets the active Navigation item
4272 * @return {Roo.bootstrap.NavItem} the current navitem
4274 getActive : function()
4278 Roo.each(this.navItems, function(v){
4289 indexOfNav : function()
4293 Roo.each(this.navItems, function(v,i){
4304 * adds a Navigation item
4305 * @param {Roo.bootstrap.NavItem} the navitem to add
4307 addItem : function(cfg)
4309 var cn = new Roo.bootstrap.NavItem(cfg);
4311 cn.parentId = this.id;
4312 cn.onRender(this.el, null);
4316 * register a Navigation item
4317 * @param {Roo.bootstrap.NavItem} the navitem to add
4319 register : function(item)
4321 this.navItems.push( item);
4322 item.navId = this.navId;
4327 * clear all the Navigation item
4330 clearAll : function()
4333 this.el.dom.innerHTML = '';
4336 getNavItem: function(tabId)
4339 Roo.each(this.navItems, function(e) {
4340 if (e.tabId == tabId) {
4350 setActiveNext : function()
4352 var i = this.indexOfNav(this.getActive());
4353 if (i > this.navItems.length) {
4356 this.setActiveItem(this.navItems[i+1]);
4358 setActivePrev : function()
4360 var i = this.indexOfNav(this.getActive());
4364 this.setActiveItem(this.navItems[i-1]);
4366 clearWasActive : function(except) {
4367 Roo.each(this.navItems, function(e) {
4368 if (e.tabId != except.tabId && e.was_active) {
4369 e.was_active = false;
4376 getWasActive : function ()
4379 Roo.each(this.navItems, function(e) {
4394 Roo.apply(Roo.bootstrap.NavGroup, {
4398 * register a Navigation Group
4399 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4401 register : function(navgrp)
4403 this.groups[navgrp.navId] = navgrp;
4407 * fetch a Navigation Group based on the navigation ID
4408 * @param {string} the navgroup to add
4409 * @returns {Roo.bootstrap.NavGroup} the navgroup
4411 get: function(navId) {
4412 if (typeof(this.groups[navId]) == 'undefined') {
4414 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4416 return this.groups[navId] ;
4431 * @class Roo.bootstrap.NavItem
4432 * @extends Roo.bootstrap.Component
4433 * Bootstrap Navbar.NavItem class
4434 * @cfg {String} href link to
4435 * @cfg {String} html content of button
4436 * @cfg {String} badge text inside badge
4437 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4438 * @cfg {String} glyphicon name of glyphicon
4439 * @cfg {String} icon name of font awesome icon
4440 * @cfg {Boolean} active Is item active
4441 * @cfg {Boolean} disabled Is item disabled
4443 * @cfg {Boolean} preventDefault (true | false) default false
4444 * @cfg {String} tabId the tab that this item activates.
4445 * @cfg {String} tagtype (a|span) render as a href or span?
4446 * @cfg {Boolean} animateRef (true|false) link to element default false
4449 * Create a new Navbar Item
4450 * @param {Object} config The config object
4452 Roo.bootstrap.NavItem = function(config){
4453 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4458 * The raw click event for the entire grid.
4459 * @param {Roo.EventObject} e
4464 * Fires when the active item active state changes
4465 * @param {Roo.bootstrap.NavItem} this
4466 * @param {boolean} state the new state
4472 * Fires when scroll to element
4473 * @param {Roo.bootstrap.NavItem} this
4474 * @param {Object} options
4475 * @param {Roo.EventObject} e
4483 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4491 preventDefault : false,
4498 getAutoCreate : function(){
4507 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4509 if (this.disabled) {
4510 cfg.cls += ' disabled';
4513 if (this.href || this.html || this.glyphicon || this.icon) {
4517 href : this.href || "#",
4518 html: this.html || ''
4523 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4526 if(this.glyphicon) {
4527 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4532 cfg.cn[0].html += " <span class='caret'></span>";
4536 if (this.badge !== '') {
4538 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4546 initEvents: function()
4548 if (typeof (this.menu) != 'undefined') {
4549 this.menu.parentType = this.xtype;
4550 this.menu.triggerEl = this.el;
4551 this.menu = this.addxtype(Roo.apply({}, this.menu));
4554 this.el.select('a',true).on('click', this.onClick, this);
4556 if(this.tagtype == 'span'){
4557 this.el.select('span',true).on('click', this.onClick, this);
4560 // at this point parent should be available..
4561 this.parent().register(this);
4564 onClick : function(e)
4566 if (e.getTarget('.dropdown-menu-item')) {
4567 // did you click on a menu itemm.... - then don't trigger onclick..
4572 this.preventDefault ||
4575 Roo.log("NavItem - prevent Default?");
4579 if (this.disabled) {
4583 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4584 if (tg && tg.transition) {
4585 Roo.log("waiting for the transitionend");
4591 //Roo.log("fire event clicked");
4592 if(this.fireEvent('click', this, e) === false){
4596 if(this.tagtype == 'span'){
4600 //Roo.log(this.href);
4601 var ael = this.el.select('a',true).first();
4604 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4605 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4606 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4607 return; // ignore... - it's a 'hash' to another page.
4609 Roo.log("NavItem - prevent Default?");
4611 this.scrollToElement(e);
4615 var p = this.parent();
4617 if (['tabs','pills'].indexOf(p.type)!==-1) {
4618 if (typeof(p.setActiveItem) !== 'undefined') {
4619 p.setActiveItem(this);
4623 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4624 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4625 // remove the collapsed menu expand...
4626 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4630 isActive: function () {
4633 setActive : function(state, fire, is_was_active)
4635 if (this.active && !state && this.navId) {
4636 this.was_active = true;
4637 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4639 nv.clearWasActive(this);
4643 this.active = state;
4646 this.el.removeClass('active');
4647 } else if (!this.el.hasClass('active')) {
4648 this.el.addClass('active');
4651 this.fireEvent('changed', this, state);
4654 // show a panel if it's registered and related..
4656 if (!this.navId || !this.tabId || !state || is_was_active) {
4660 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4664 var pan = tg.getPanelByName(this.tabId);
4668 // if we can not flip to new panel - go back to old nav highlight..
4669 if (false == tg.showPanel(pan)) {
4670 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4672 var onav = nv.getWasActive();
4674 onav.setActive(true, false, true);
4683 // this should not be here...
4684 setDisabled : function(state)
4686 this.disabled = state;
4688 this.el.removeClass('disabled');
4689 } else if (!this.el.hasClass('disabled')) {
4690 this.el.addClass('disabled');
4696 * Fetch the element to display the tooltip on.
4697 * @return {Roo.Element} defaults to this.el
4699 tooltipEl : function()
4701 return this.el.select('' + this.tagtype + '', true).first();
4704 scrollToElement : function(e)
4706 var c = document.body;
4709 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4711 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4712 c = document.documentElement;
4715 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4721 var o = target.calcOffsetsTo(c);
4728 this.fireEvent('scrollto', this, options, e);
4730 Roo.get(c).scrollTo('top', options.value, true);
4743 * <span> icon </span>
4744 * <span> text </span>
4745 * <span>badge </span>
4749 * @class Roo.bootstrap.NavSidebarItem
4750 * @extends Roo.bootstrap.NavItem
4751 * Bootstrap Navbar.NavSidebarItem class
4752 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4753 * {Boolean} open is the menu open
4754 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4755 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4756 * {String} buttonSize (sm|md|lg)the extra classes for the button
4757 * {Boolean} showArrow show arrow next to the text (default true)
4759 * Create a new Navbar Button
4760 * @param {Object} config The config object
4762 Roo.bootstrap.NavSidebarItem = function(config){
4763 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4768 * The raw click event for the entire grid.
4769 * @param {Roo.EventObject} e
4774 * Fires when the active item active state changes
4775 * @param {Roo.bootstrap.NavSidebarItem} this
4776 * @param {boolean} state the new state
4784 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4786 badgeWeight : 'default',
4792 buttonWeight : 'default',
4798 getAutoCreate : function(){
4803 href : this.href || '#',
4809 if(this.buttonView){
4812 href : this.href || '#',
4813 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4826 cfg.cls += ' active';
4829 if (this.disabled) {
4830 cfg.cls += ' disabled';
4833 cfg.cls += ' open x-open';
4836 if (this.glyphicon || this.icon) {
4837 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4838 a.cn.push({ tag : 'i', cls : c }) ;
4841 if(!this.buttonView){
4844 html : this.html || ''
4851 if (this.badge !== '') {
4852 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4858 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4861 a.cls += ' dropdown-toggle treeview' ;
4867 initEvents : function()
4869 if (typeof (this.menu) != 'undefined') {
4870 this.menu.parentType = this.xtype;
4871 this.menu.triggerEl = this.el;
4872 this.menu = this.addxtype(Roo.apply({}, this.menu));
4875 this.el.on('click', this.onClick, this);
4877 if(this.badge !== ''){
4878 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4883 onClick : function(e)
4890 if(this.preventDefault){
4894 this.fireEvent('click', this);
4897 disable : function()
4899 this.setDisabled(true);
4904 this.setDisabled(false);
4907 setDisabled : function(state)
4909 if(this.disabled == state){
4913 this.disabled = state;
4916 this.el.addClass('disabled');
4920 this.el.removeClass('disabled');
4925 setActive : function(state)
4927 if(this.active == state){
4931 this.active = state;
4934 this.el.addClass('active');
4938 this.el.removeClass('active');
4943 isActive: function ()
4948 setBadge : function(str)
4954 this.badgeEl.dom.innerHTML = str;
4971 * @class Roo.bootstrap.Row
4972 * @extends Roo.bootstrap.Component
4973 * Bootstrap Row class (contains columns...)
4977 * @param {Object} config The config object
4980 Roo.bootstrap.Row = function(config){
4981 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4984 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4986 getAutoCreate : function(){
5005 * @class Roo.bootstrap.Element
5006 * @extends Roo.bootstrap.Component
5007 * Bootstrap Element class
5008 * @cfg {String} html contents of the element
5009 * @cfg {String} tag tag of the element
5010 * @cfg {String} cls class of the element
5011 * @cfg {Boolean} preventDefault (true|false) default false
5012 * @cfg {Boolean} clickable (true|false) default false
5015 * Create a new Element
5016 * @param {Object} config The config object
5019 Roo.bootstrap.Element = function(config){
5020 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5026 * When a element is chick
5027 * @param {Roo.bootstrap.Element} this
5028 * @param {Roo.EventObject} e
5034 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5039 preventDefault: false,
5042 getAutoCreate : function(){
5046 // cls: this.cls, double assign in parent class Component.js :: onRender
5053 initEvents: function()
5055 Roo.bootstrap.Element.superclass.initEvents.call(this);
5058 this.el.on('click', this.onClick, this);
5063 onClick : function(e)
5065 if(this.preventDefault){
5069 this.fireEvent('click', this, e);
5072 getValue : function()
5074 return this.el.dom.innerHTML;
5077 setValue : function(value)
5079 this.el.dom.innerHTML = value;
5094 * @class Roo.bootstrap.Pagination
5095 * @extends Roo.bootstrap.Component
5096 * Bootstrap Pagination class
5097 * @cfg {String} size xs | sm | md | lg
5098 * @cfg {Boolean} inverse false | true
5101 * Create a new Pagination
5102 * @param {Object} config The config object
5105 Roo.bootstrap.Pagination = function(config){
5106 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5109 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5115 getAutoCreate : function(){
5121 cfg.cls += ' inverse';
5127 cfg.cls += " " + this.cls;
5145 * @class Roo.bootstrap.PaginationItem
5146 * @extends Roo.bootstrap.Component
5147 * Bootstrap PaginationItem class
5148 * @cfg {String} html text
5149 * @cfg {String} href the link
5150 * @cfg {Boolean} preventDefault (true | false) default true
5151 * @cfg {Boolean} active (true | false) default false
5152 * @cfg {Boolean} disabled default false
5156 * Create a new PaginationItem
5157 * @param {Object} config The config object
5161 Roo.bootstrap.PaginationItem = function(config){
5162 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5167 * The raw click event for the entire grid.
5168 * @param {Roo.EventObject} e
5174 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5178 preventDefault: true,
5183 getAutoCreate : function(){
5189 href : this.href ? this.href : '#',
5190 html : this.html ? this.html : ''
5200 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5204 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5210 initEvents: function() {
5212 this.el.on('click', this.onClick, this);
5215 onClick : function(e)
5217 Roo.log('PaginationItem on click ');
5218 if(this.preventDefault){
5226 this.fireEvent('click', this, e);
5242 * @class Roo.bootstrap.Slider
5243 * @extends Roo.bootstrap.Component
5244 * Bootstrap Slider class
5247 * Create a new Slider
5248 * @param {Object} config The config object
5251 Roo.bootstrap.Slider = function(config){
5252 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5255 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5257 getAutoCreate : function(){
5261 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5265 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5277 * Ext JS Library 1.1.1
5278 * Copyright(c) 2006-2007, Ext JS, LLC.
5280 * Originally Released Under LGPL - original licence link has changed is not relivant.
5283 * <script type="text/javascript">
5288 * @class Roo.grid.ColumnModel
5289 * @extends Roo.util.Observable
5290 * This is the default implementation of a ColumnModel used by the Grid. It defines
5291 * the columns in the grid.
5294 var colModel = new Roo.grid.ColumnModel([
5295 {header: "Ticker", width: 60, sortable: true, locked: true},
5296 {header: "Company Name", width: 150, sortable: true},
5297 {header: "Market Cap.", width: 100, sortable: true},
5298 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5299 {header: "Employees", width: 100, sortable: true, resizable: false}
5304 * The config options listed for this class are options which may appear in each
5305 * individual column definition.
5306 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5308 * @param {Object} config An Array of column config objects. See this class's
5309 * config objects for details.
5311 Roo.grid.ColumnModel = function(config){
5313 * The config passed into the constructor
5315 this.config = config;
5318 // if no id, create one
5319 // if the column does not have a dataIndex mapping,
5320 // map it to the order it is in the config
5321 for(var i = 0, len = config.length; i < len; i++){
5323 if(typeof c.dataIndex == "undefined"){
5326 if(typeof c.renderer == "string"){
5327 c.renderer = Roo.util.Format[c.renderer];
5329 if(typeof c.id == "undefined"){
5332 if(c.editor && c.editor.xtype){
5333 c.editor = Roo.factory(c.editor, Roo.grid);
5335 if(c.editor && c.editor.isFormField){
5336 c.editor = new Roo.grid.GridEditor(c.editor);
5338 this.lookup[c.id] = c;
5342 * The width of columns which have no width specified (defaults to 100)
5345 this.defaultWidth = 100;
5348 * Default sortable of columns which have no sortable specified (defaults to false)
5351 this.defaultSortable = false;
5355 * @event widthchange
5356 * Fires when the width of a column changes.
5357 * @param {ColumnModel} this
5358 * @param {Number} columnIndex The column index
5359 * @param {Number} newWidth The new width
5361 "widthchange": true,
5363 * @event headerchange
5364 * Fires when the text of a header changes.
5365 * @param {ColumnModel} this
5366 * @param {Number} columnIndex The column index
5367 * @param {Number} newText The new header text
5369 "headerchange": true,
5371 * @event hiddenchange
5372 * Fires when a column is hidden or "unhidden".
5373 * @param {ColumnModel} this
5374 * @param {Number} columnIndex The column index
5375 * @param {Boolean} hidden true if hidden, false otherwise
5377 "hiddenchange": true,
5379 * @event columnmoved
5380 * Fires when a column is moved.
5381 * @param {ColumnModel} this
5382 * @param {Number} oldIndex
5383 * @param {Number} newIndex
5385 "columnmoved" : true,
5387 * @event columlockchange
5388 * Fires when a column's locked state is changed
5389 * @param {ColumnModel} this
5390 * @param {Number} colIndex
5391 * @param {Boolean} locked true if locked
5393 "columnlockchange" : true
5395 Roo.grid.ColumnModel.superclass.constructor.call(this);
5397 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5399 * @cfg {String} header The header text to display in the Grid view.
5402 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5403 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5404 * specified, the column's index is used as an index into the Record's data Array.
5407 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5408 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5411 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5412 * Defaults to the value of the {@link #defaultSortable} property.
5413 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5416 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5419 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5422 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5425 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5428 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5429 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5430 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5431 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5434 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5437 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5440 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5443 * @cfg {String} cursor (Optional)
5446 * @cfg {String} tooltip (Optional)
5449 * @cfg {Number} xs (Optional)
5452 * @cfg {Number} sm (Optional)
5455 * @cfg {Number} md (Optional)
5458 * @cfg {Number} lg (Optional)
5461 * Returns the id of the column at the specified index.
5462 * @param {Number} index The column index
5463 * @return {String} the id
5465 getColumnId : function(index){
5466 return this.config[index].id;
5470 * Returns the column for a specified id.
5471 * @param {String} id The column id
5472 * @return {Object} the column
5474 getColumnById : function(id){
5475 return this.lookup[id];
5480 * Returns the column for a specified dataIndex.
5481 * @param {String} dataIndex The column dataIndex
5482 * @return {Object|Boolean} the column or false if not found
5484 getColumnByDataIndex: function(dataIndex){
5485 var index = this.findColumnIndex(dataIndex);
5486 return index > -1 ? this.config[index] : false;
5490 * Returns the index for a specified column id.
5491 * @param {String} id The column id
5492 * @return {Number} the index, or -1 if not found
5494 getIndexById : function(id){
5495 for(var i = 0, len = this.config.length; i < len; i++){
5496 if(this.config[i].id == id){
5504 * Returns the index for a specified column dataIndex.
5505 * @param {String} dataIndex The column dataIndex
5506 * @return {Number} the index, or -1 if not found
5509 findColumnIndex : function(dataIndex){
5510 for(var i = 0, len = this.config.length; i < len; i++){
5511 if(this.config[i].dataIndex == dataIndex){
5519 moveColumn : function(oldIndex, newIndex){
5520 var c = this.config[oldIndex];
5521 this.config.splice(oldIndex, 1);
5522 this.config.splice(newIndex, 0, c);
5523 this.dataMap = null;
5524 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5527 isLocked : function(colIndex){
5528 return this.config[colIndex].locked === true;
5531 setLocked : function(colIndex, value, suppressEvent){
5532 if(this.isLocked(colIndex) == value){
5535 this.config[colIndex].locked = value;
5537 this.fireEvent("columnlockchange", this, colIndex, value);
5541 getTotalLockedWidth : function(){
5543 for(var i = 0; i < this.config.length; i++){
5544 if(this.isLocked(i) && !this.isHidden(i)){
5545 this.totalWidth += this.getColumnWidth(i);
5551 getLockedCount : function(){
5552 for(var i = 0, len = this.config.length; i < len; i++){
5553 if(!this.isLocked(i)){
5558 return this.config.length;
5562 * Returns the number of columns.
5565 getColumnCount : function(visibleOnly){
5566 if(visibleOnly === true){
5568 for(var i = 0, len = this.config.length; i < len; i++){
5569 if(!this.isHidden(i)){
5575 return this.config.length;
5579 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5580 * @param {Function} fn
5581 * @param {Object} scope (optional)
5582 * @return {Array} result
5584 getColumnsBy : function(fn, scope){
5586 for(var i = 0, len = this.config.length; i < len; i++){
5587 var c = this.config[i];
5588 if(fn.call(scope||this, c, i) === true){
5596 * Returns true if the specified column is sortable.
5597 * @param {Number} col The column index
5600 isSortable : function(col){
5601 if(typeof this.config[col].sortable == "undefined"){
5602 return this.defaultSortable;
5604 return this.config[col].sortable;
5608 * Returns the rendering (formatting) function defined for the column.
5609 * @param {Number} col The column index.
5610 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5612 getRenderer : function(col){
5613 if(!this.config[col].renderer){
5614 return Roo.grid.ColumnModel.defaultRenderer;
5616 return this.config[col].renderer;
5620 * Sets the rendering (formatting) function for a column.
5621 * @param {Number} col The column index
5622 * @param {Function} fn The function to use to process the cell's raw data
5623 * to return HTML markup for the grid view. The render function is called with
5624 * the following parameters:<ul>
5625 * <li>Data value.</li>
5626 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5627 * <li>css A CSS style string to apply to the table cell.</li>
5628 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5629 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5630 * <li>Row index</li>
5631 * <li>Column index</li>
5632 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5634 setRenderer : function(col, fn){
5635 this.config[col].renderer = fn;
5639 * Returns the width for the specified column.
5640 * @param {Number} col The column index
5643 getColumnWidth : function(col){
5644 return this.config[col].width * 1 || this.defaultWidth;
5648 * Sets the width for a column.
5649 * @param {Number} col The column index
5650 * @param {Number} width The new width
5652 setColumnWidth : function(col, width, suppressEvent){
5653 this.config[col].width = width;
5654 this.totalWidth = null;
5656 this.fireEvent("widthchange", this, col, width);
5661 * Returns the total width of all columns.
5662 * @param {Boolean} includeHidden True to include hidden column widths
5665 getTotalWidth : function(includeHidden){
5666 if(!this.totalWidth){
5667 this.totalWidth = 0;
5668 for(var i = 0, len = this.config.length; i < len; i++){
5669 if(includeHidden || !this.isHidden(i)){
5670 this.totalWidth += this.getColumnWidth(i);
5674 return this.totalWidth;
5678 * Returns the header for the specified column.
5679 * @param {Number} col The column index
5682 getColumnHeader : function(col){
5683 return this.config[col].header;
5687 * Sets the header for a column.
5688 * @param {Number} col The column index
5689 * @param {String} header The new header
5691 setColumnHeader : function(col, header){
5692 this.config[col].header = header;
5693 this.fireEvent("headerchange", this, col, header);
5697 * Returns the tooltip for the specified column.
5698 * @param {Number} col The column index
5701 getColumnTooltip : function(col){
5702 return this.config[col].tooltip;
5705 * Sets the tooltip for a column.
5706 * @param {Number} col The column index
5707 * @param {String} tooltip The new tooltip
5709 setColumnTooltip : function(col, tooltip){
5710 this.config[col].tooltip = tooltip;
5714 * Returns the dataIndex for the specified column.
5715 * @param {Number} col The column index
5718 getDataIndex : function(col){
5719 return this.config[col].dataIndex;
5723 * Sets the dataIndex for a column.
5724 * @param {Number} col The column index
5725 * @param {Number} dataIndex The new dataIndex
5727 setDataIndex : function(col, dataIndex){
5728 this.config[col].dataIndex = dataIndex;
5734 * Returns true if the cell is editable.
5735 * @param {Number} colIndex The column index
5736 * @param {Number} rowIndex The row index - this is nto actually used..?
5739 isCellEditable : function(colIndex, rowIndex){
5740 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5744 * Returns the editor defined for the cell/column.
5745 * return false or null to disable editing.
5746 * @param {Number} colIndex The column index
5747 * @param {Number} rowIndex The row index
5750 getCellEditor : function(colIndex, rowIndex){
5751 return this.config[colIndex].editor;
5755 * Sets if a column is editable.
5756 * @param {Number} col The column index
5757 * @param {Boolean} editable True if the column is editable
5759 setEditable : function(col, editable){
5760 this.config[col].editable = editable;
5765 * Returns true if the column is hidden.
5766 * @param {Number} colIndex The column index
5769 isHidden : function(colIndex){
5770 return this.config[colIndex].hidden;
5775 * Returns true if the column width cannot be changed
5777 isFixed : function(colIndex){
5778 return this.config[colIndex].fixed;
5782 * Returns true if the column can be resized
5785 isResizable : function(colIndex){
5786 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5789 * Sets if a column is hidden.
5790 * @param {Number} colIndex The column index
5791 * @param {Boolean} hidden True if the column is hidden
5793 setHidden : function(colIndex, hidden){
5794 this.config[colIndex].hidden = hidden;
5795 this.totalWidth = null;
5796 this.fireEvent("hiddenchange", this, colIndex, hidden);
5800 * Sets the editor for a column.
5801 * @param {Number} col The column index
5802 * @param {Object} editor The editor object
5804 setEditor : function(col, editor){
5805 this.config[col].editor = editor;
5809 Roo.grid.ColumnModel.defaultRenderer = function(value)
5811 if(typeof value == "object") {
5814 if(typeof value == "string" && value.length < 1){
5818 return String.format("{0}", value);
5821 // Alias for backwards compatibility
5822 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5825 * Ext JS Library 1.1.1
5826 * Copyright(c) 2006-2007, Ext JS, LLC.
5828 * Originally Released Under LGPL - original licence link has changed is not relivant.
5831 * <script type="text/javascript">
5835 * @class Roo.LoadMask
5836 * A simple utility class for generically masking elements while loading data. If the element being masked has
5837 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5838 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5839 * element's UpdateManager load indicator and will be destroyed after the initial load.
5841 * Create a new LoadMask
5842 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5843 * @param {Object} config The config object
5845 Roo.LoadMask = function(el, config){
5846 this.el = Roo.get(el);
5847 Roo.apply(this, config);
5849 this.store.on('beforeload', this.onBeforeLoad, this);
5850 this.store.on('load', this.onLoad, this);
5851 this.store.on('loadexception', this.onLoadException, this);
5852 this.removeMask = false;
5854 var um = this.el.getUpdateManager();
5855 um.showLoadIndicator = false; // disable the default indicator
5856 um.on('beforeupdate', this.onBeforeLoad, this);
5857 um.on('update', this.onLoad, this);
5858 um.on('failure', this.onLoad, this);
5859 this.removeMask = true;
5863 Roo.LoadMask.prototype = {
5865 * @cfg {Boolean} removeMask
5866 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5867 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5871 * The text to display in a centered loading message box (defaults to 'Loading...')
5875 * @cfg {String} msgCls
5876 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5878 msgCls : 'x-mask-loading',
5881 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5887 * Disables the mask to prevent it from being displayed
5889 disable : function(){
5890 this.disabled = true;
5894 * Enables the mask so that it can be displayed
5896 enable : function(){
5897 this.disabled = false;
5900 onLoadException : function()
5904 if (typeof(arguments[3]) != 'undefined') {
5905 Roo.MessageBox.alert("Error loading",arguments[3]);
5909 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5910 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5917 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5922 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5926 onBeforeLoad : function(){
5928 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5933 destroy : function(){
5935 this.store.un('beforeload', this.onBeforeLoad, this);
5936 this.store.un('load', this.onLoad, this);
5937 this.store.un('loadexception', this.onLoadException, this);
5939 var um = this.el.getUpdateManager();
5940 um.un('beforeupdate', this.onBeforeLoad, this);
5941 um.un('update', this.onLoad, this);
5942 um.un('failure', this.onLoad, this);
5953 * @class Roo.bootstrap.Table
5954 * @extends Roo.bootstrap.Component
5955 * Bootstrap Table class
5956 * @cfg {String} cls table class
5957 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5958 * @cfg {String} bgcolor Specifies the background color for a table
5959 * @cfg {Number} border Specifies whether the table cells should have borders or not
5960 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5961 * @cfg {Number} cellspacing Specifies the space between cells
5962 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5963 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5964 * @cfg {String} sortable Specifies that the table should be sortable
5965 * @cfg {String} summary Specifies a summary of the content of a table
5966 * @cfg {Number} width Specifies the width of a table
5967 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5969 * @cfg {boolean} striped Should the rows be alternative striped
5970 * @cfg {boolean} bordered Add borders to the table
5971 * @cfg {boolean} hover Add hover highlighting
5972 * @cfg {boolean} condensed Format condensed
5973 * @cfg {boolean} responsive Format condensed
5974 * @cfg {Boolean} loadMask (true|false) default false
5975 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5976 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5977 * @cfg {Boolean} rowSelection (true|false) default false
5978 * @cfg {Boolean} cellSelection (true|false) default false
5979 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5980 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5981 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5982 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
5986 * Create a new Table
5987 * @param {Object} config The config object
5990 Roo.bootstrap.Table = function(config){
5991 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5996 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5997 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5998 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5999 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6001 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6003 this.sm.grid = this;
6004 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6005 this.sm = this.selModel;
6006 this.sm.xmodule = this.xmodule || false;
6009 if (this.cm && typeof(this.cm.config) == 'undefined') {
6010 this.colModel = new Roo.grid.ColumnModel(this.cm);
6011 this.cm = this.colModel;
6012 this.cm.xmodule = this.xmodule || false;
6015 this.store= Roo.factory(this.store, Roo.data);
6016 this.ds = this.store;
6017 this.ds.xmodule = this.xmodule || false;
6020 if (this.footer && this.store) {
6021 this.footer.dataSource = this.ds;
6022 this.footer = Roo.factory(this.footer);
6029 * Fires when a cell is clicked
6030 * @param {Roo.bootstrap.Table} this
6031 * @param {Roo.Element} el
6032 * @param {Number} rowIndex
6033 * @param {Number} columnIndex
6034 * @param {Roo.EventObject} e
6038 * @event celldblclick
6039 * Fires when a cell is double clicked
6040 * @param {Roo.bootstrap.Table} this
6041 * @param {Roo.Element} el
6042 * @param {Number} rowIndex
6043 * @param {Number} columnIndex
6044 * @param {Roo.EventObject} e
6046 "celldblclick" : true,
6049 * Fires when a row is clicked
6050 * @param {Roo.bootstrap.Table} this
6051 * @param {Roo.Element} el
6052 * @param {Number} rowIndex
6053 * @param {Roo.EventObject} e
6057 * @event rowdblclick
6058 * Fires when a row is double clicked
6059 * @param {Roo.bootstrap.Table} this
6060 * @param {Roo.Element} el
6061 * @param {Number} rowIndex
6062 * @param {Roo.EventObject} e
6064 "rowdblclick" : true,
6067 * Fires when a mouseover occur
6068 * @param {Roo.bootstrap.Table} this
6069 * @param {Roo.Element} el
6070 * @param {Number} rowIndex
6071 * @param {Number} columnIndex
6072 * @param {Roo.EventObject} e
6077 * Fires when a mouseout occur
6078 * @param {Roo.bootstrap.Table} this
6079 * @param {Roo.Element} el
6080 * @param {Number} rowIndex
6081 * @param {Number} columnIndex
6082 * @param {Roo.EventObject} e
6087 * Fires when a row is rendered, so you can change add a style to it.
6088 * @param {Roo.bootstrap.Table} this
6089 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6093 * @event rowsrendered
6094 * Fires when all the rows have been rendered
6095 * @param {Roo.bootstrap.Table} this
6097 'rowsrendered' : true,
6099 * @event contextmenu
6100 * The raw contextmenu event for the entire grid.
6101 * @param {Roo.EventObject} e
6103 "contextmenu" : true,
6105 * @event rowcontextmenu
6106 * Fires when a row is right clicked
6107 * @param {Roo.bootstrap.Table} this
6108 * @param {Number} rowIndex
6109 * @param {Roo.EventObject} e
6111 "rowcontextmenu" : true,
6113 * @event cellcontextmenu
6114 * Fires when a cell is right clicked
6115 * @param {Roo.bootstrap.Table} this
6116 * @param {Number} rowIndex
6117 * @param {Number} cellIndex
6118 * @param {Roo.EventObject} e
6120 "cellcontextmenu" : true,
6122 * @event headercontextmenu
6123 * Fires when a header is right clicked
6124 * @param {Roo.bootstrap.Table} this
6125 * @param {Number} columnIndex
6126 * @param {Roo.EventObject} e
6128 "headercontextmenu" : true
6132 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6158 rowSelection : false,
6159 cellSelection : false,
6162 // Roo.Element - the tbody
6164 // Roo.Element - thead element
6167 container: false, // used by gridpanel...
6173 auto_hide_footer : false,
6175 getAutoCreate : function()
6177 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6184 if (this.scrollBody) {
6185 cfg.cls += ' table-body-fixed';
6188 cfg.cls += ' table-striped';
6192 cfg.cls += ' table-hover';
6194 if (this.bordered) {
6195 cfg.cls += ' table-bordered';
6197 if (this.condensed) {
6198 cfg.cls += ' table-condensed';
6200 if (this.responsive) {
6201 cfg.cls += ' table-responsive';
6205 cfg.cls+= ' ' +this.cls;
6208 // this lot should be simplifed...
6221 ].forEach(function(k) {
6229 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6232 if(this.store || this.cm){
6233 if(this.headerShow){
6234 cfg.cn.push(this.renderHeader());
6237 cfg.cn.push(this.renderBody());
6239 if(this.footerShow){
6240 cfg.cn.push(this.renderFooter());
6242 // where does this come from?
6243 //cfg.cls+= ' TableGrid';
6246 return { cn : [ cfg ] };
6249 initEvents : function()
6251 if(!this.store || !this.cm){
6254 if (this.selModel) {
6255 this.selModel.initEvents();
6259 //Roo.log('initEvents with ds!!!!');
6261 this.mainBody = this.el.select('tbody', true).first();
6262 this.mainHead = this.el.select('thead', true).first();
6263 this.mainFoot = this.el.select('tfoot', true).first();
6269 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6270 e.on('click', _this.sort, _this);
6273 this.mainBody.on("click", this.onClick, this);
6274 this.mainBody.on("dblclick", this.onDblClick, this);
6276 // why is this done????? = it breaks dialogs??
6277 //this.parent().el.setStyle('position', 'relative');
6281 this.footer.parentId = this.id;
6282 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6285 this.el.select('tfoot tr td').first().addClass('hide');
6290 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6293 this.store.on('load', this.onLoad, this);
6294 this.store.on('beforeload', this.onBeforeLoad, this);
6295 this.store.on('update', this.onUpdate, this);
6296 this.store.on('add', this.onAdd, this);
6297 this.store.on("clear", this.clear, this);
6299 this.el.on("contextmenu", this.onContextMenu, this);
6301 this.mainBody.on('scroll', this.onBodyScroll, this);
6303 this.cm.on("headerchange", this.onHeaderChange, this);
6305 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6309 onContextMenu : function(e, t)
6311 this.processEvent("contextmenu", e);
6314 processEvent : function(name, e)
6316 if (name != 'touchstart' ) {
6317 this.fireEvent(name, e);
6320 var t = e.getTarget();
6322 var cell = Roo.get(t);
6328 if(cell.findParent('tfoot', false, true)){
6332 if(cell.findParent('thead', false, true)){
6334 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6335 cell = Roo.get(t).findParent('th', false, true);
6337 Roo.log("failed to find th in thead?");
6338 Roo.log(e.getTarget());
6343 var cellIndex = cell.dom.cellIndex;
6345 var ename = name == 'touchstart' ? 'click' : name;
6346 this.fireEvent("header" + ename, this, cellIndex, e);
6351 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6352 cell = Roo.get(t).findParent('td', false, true);
6354 Roo.log("failed to find th in tbody?");
6355 Roo.log(e.getTarget());
6360 var row = cell.findParent('tr', false, true);
6361 var cellIndex = cell.dom.cellIndex;
6362 var rowIndex = row.dom.rowIndex - 1;
6366 this.fireEvent("row" + name, this, rowIndex, e);
6370 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6376 onMouseover : function(e, el)
6378 var cell = Roo.get(el);
6384 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6385 cell = cell.findParent('td', false, true);
6388 var row = cell.findParent('tr', false, true);
6389 var cellIndex = cell.dom.cellIndex;
6390 var rowIndex = row.dom.rowIndex - 1; // start from 0
6392 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6396 onMouseout : function(e, el)
6398 var cell = Roo.get(el);
6404 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6405 cell = cell.findParent('td', false, true);
6408 var row = cell.findParent('tr', false, true);
6409 var cellIndex = cell.dom.cellIndex;
6410 var rowIndex = row.dom.rowIndex - 1; // start from 0
6412 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6416 onClick : function(e, el)
6418 var cell = Roo.get(el);
6420 if(!cell || (!this.cellSelection && !this.rowSelection)){
6424 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6425 cell = cell.findParent('td', false, true);
6428 if(!cell || typeof(cell) == 'undefined'){
6432 var row = cell.findParent('tr', false, true);
6434 if(!row || typeof(row) == 'undefined'){
6438 var cellIndex = cell.dom.cellIndex;
6439 var rowIndex = this.getRowIndex(row);
6441 // why??? - should these not be based on SelectionModel?
6442 if(this.cellSelection){
6443 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6446 if(this.rowSelection){
6447 this.fireEvent('rowclick', this, row, rowIndex, e);
6453 onDblClick : function(e,el)
6455 var cell = Roo.get(el);
6457 if(!cell || (!this.cellSelection && !this.rowSelection)){
6461 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6462 cell = cell.findParent('td', false, true);
6465 if(!cell || typeof(cell) == 'undefined'){
6469 var row = cell.findParent('tr', false, true);
6471 if(!row || typeof(row) == 'undefined'){
6475 var cellIndex = cell.dom.cellIndex;
6476 var rowIndex = this.getRowIndex(row);
6478 if(this.cellSelection){
6479 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6482 if(this.rowSelection){
6483 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6487 sort : function(e,el)
6489 var col = Roo.get(el);
6491 if(!col.hasClass('sortable')){
6495 var sort = col.attr('sort');
6498 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6502 this.store.sortInfo = {field : sort, direction : dir};
6505 Roo.log("calling footer first");
6506 this.footer.onClick('first');
6509 this.store.load({ params : { start : 0 } });
6513 renderHeader : function()
6521 this.totalWidth = 0;
6523 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6525 var config = cm.config[i];
6529 cls : 'x-hcol-' + i,
6531 html: cm.getColumnHeader(i)
6536 if(typeof(config.sortable) != 'undefined' && config.sortable){
6538 c.html = '<i class="glyphicon"></i>' + c.html;
6541 if(typeof(config.lgHeader) != 'undefined'){
6542 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6545 if(typeof(config.mdHeader) != 'undefined'){
6546 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6549 if(typeof(config.smHeader) != 'undefined'){
6550 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6553 if(typeof(config.xsHeader) != 'undefined'){
6554 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6561 if(typeof(config.tooltip) != 'undefined'){
6562 c.tooltip = config.tooltip;
6565 if(typeof(config.colspan) != 'undefined'){
6566 c.colspan = config.colspan;
6569 if(typeof(config.hidden) != 'undefined' && config.hidden){
6570 c.style += ' display:none;';
6573 if(typeof(config.dataIndex) != 'undefined'){
6574 c.sort = config.dataIndex;
6579 if(typeof(config.align) != 'undefined' && config.align.length){
6580 c.style += ' text-align:' + config.align + ';';
6583 if(typeof(config.width) != 'undefined'){
6584 c.style += ' width:' + config.width + 'px;';
6585 this.totalWidth += config.width;
6587 this.totalWidth += 100; // assume minimum of 100 per column?
6590 if(typeof(config.cls) != 'undefined'){
6591 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6594 ['xs','sm','md','lg'].map(function(size){
6596 if(typeof(config[size]) == 'undefined'){
6600 if (!config[size]) { // 0 = hidden
6601 c.cls += ' hidden-' + size;
6605 c.cls += ' col-' + size + '-' + config[size];
6615 renderBody : function()
6625 colspan : this.cm.getColumnCount()
6635 renderFooter : function()
6645 colspan : this.cm.getColumnCount()
6659 // Roo.log('ds onload');
6664 var ds = this.store;
6666 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6667 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6668 if (_this.store.sortInfo) {
6670 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6671 e.select('i', true).addClass(['glyphicon-arrow-up']);
6674 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6675 e.select('i', true).addClass(['glyphicon-arrow-down']);
6680 var tbody = this.mainBody;
6682 if(ds.getCount() > 0){
6683 ds.data.each(function(d,rowIndex){
6684 var row = this.renderRow(cm, ds, rowIndex);
6686 tbody.createChild(row);
6690 if(row.cellObjects.length){
6691 Roo.each(row.cellObjects, function(r){
6692 _this.renderCellObject(r);
6699 var tfoot = this.el.select('tfoot', true).first();
6701 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6703 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6705 var total = this.ds.getTotalCount();
6707 if(this.footer.pageSize < total){
6708 this.mainFoot.show();
6712 Roo.each(this.el.select('tbody td', true).elements, function(e){
6713 e.on('mouseover', _this.onMouseover, _this);
6716 Roo.each(this.el.select('tbody td', true).elements, function(e){
6717 e.on('mouseout', _this.onMouseout, _this);
6719 this.fireEvent('rowsrendered', this);
6725 onUpdate : function(ds,record)
6727 this.refreshRow(record);
6731 onRemove : function(ds, record, index, isUpdate){
6732 if(isUpdate !== true){
6733 this.fireEvent("beforerowremoved", this, index, record);
6735 var bt = this.mainBody.dom;
6737 var rows = this.el.select('tbody > tr', true).elements;
6739 if(typeof(rows[index]) != 'undefined'){
6740 bt.removeChild(rows[index].dom);
6743 // if(bt.rows[index]){
6744 // bt.removeChild(bt.rows[index]);
6747 if(isUpdate !== true){
6748 //this.stripeRows(index);
6749 //this.syncRowHeights(index, index);
6751 this.fireEvent("rowremoved", this, index, record);
6755 onAdd : function(ds, records, rowIndex)
6757 //Roo.log('on Add called');
6758 // - note this does not handle multiple adding very well..
6759 var bt = this.mainBody.dom;
6760 for (var i =0 ; i < records.length;i++) {
6761 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6762 //Roo.log(records[i]);
6763 //Roo.log(this.store.getAt(rowIndex+i));
6764 this.insertRow(this.store, rowIndex + i, false);
6771 refreshRow : function(record){
6772 var ds = this.store, index;
6773 if(typeof record == 'number'){
6775 record = ds.getAt(index);
6777 index = ds.indexOf(record);
6779 this.insertRow(ds, index, true);
6781 this.onRemove(ds, record, index+1, true);
6783 //this.syncRowHeights(index, index);
6785 this.fireEvent("rowupdated", this, index, record);
6788 insertRow : function(dm, rowIndex, isUpdate){
6791 this.fireEvent("beforerowsinserted", this, rowIndex);
6793 //var s = this.getScrollState();
6794 var row = this.renderRow(this.cm, this.store, rowIndex);
6795 // insert before rowIndex..
6796 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6800 if(row.cellObjects.length){
6801 Roo.each(row.cellObjects, function(r){
6802 _this.renderCellObject(r);
6807 this.fireEvent("rowsinserted", this, rowIndex);
6808 //this.syncRowHeights(firstRow, lastRow);
6809 //this.stripeRows(firstRow);
6816 getRowDom : function(rowIndex)
6818 var rows = this.el.select('tbody > tr', true).elements;
6820 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6823 // returns the object tree for a tr..
6826 renderRow : function(cm, ds, rowIndex)
6828 var d = ds.getAt(rowIndex);
6832 cls : 'x-row-' + rowIndex,
6836 var cellObjects = [];
6838 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6839 var config = cm.config[i];
6841 var renderer = cm.getRenderer(i);
6845 if(typeof(renderer) !== 'undefined'){
6846 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6848 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6849 // and are rendered into the cells after the row is rendered - using the id for the element.
6851 if(typeof(value) === 'object'){
6861 rowIndex : rowIndex,
6866 this.fireEvent('rowclass', this, rowcfg);
6870 cls : rowcfg.rowClass + ' x-col-' + i,
6872 html: (typeof(value) === 'object') ? '' : value
6879 if(typeof(config.colspan) != 'undefined'){
6880 td.colspan = config.colspan;
6883 if(typeof(config.hidden) != 'undefined' && config.hidden){
6884 td.style += ' display:none;';
6887 if(typeof(config.align) != 'undefined' && config.align.length){
6888 td.style += ' text-align:' + config.align + ';';
6890 if(typeof(config.valign) != 'undefined' && config.valign.length){
6891 td.style += ' vertical-align:' + config.valign + ';';
6894 if(typeof(config.width) != 'undefined'){
6895 td.style += ' width:' + config.width + 'px;';
6898 if(typeof(config.cursor) != 'undefined'){
6899 td.style += ' cursor:' + config.cursor + ';';
6902 if(typeof(config.cls) != 'undefined'){
6903 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6906 ['xs','sm','md','lg'].map(function(size){
6908 if(typeof(config[size]) == 'undefined'){
6912 if (!config[size]) { // 0 = hidden
6913 td.cls += ' hidden-' + size;
6917 td.cls += ' col-' + size + '-' + config[size];
6925 row.cellObjects = cellObjects;
6933 onBeforeLoad : function()
6942 this.el.select('tbody', true).first().dom.innerHTML = '';
6945 * Show or hide a row.
6946 * @param {Number} rowIndex to show or hide
6947 * @param {Boolean} state hide
6949 setRowVisibility : function(rowIndex, state)
6951 var bt = this.mainBody.dom;
6953 var rows = this.el.select('tbody > tr', true).elements;
6955 if(typeof(rows[rowIndex]) == 'undefined'){
6958 rows[rowIndex].dom.style.display = state ? '' : 'none';
6962 getSelectionModel : function(){
6964 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6966 return this.selModel;
6969 * Render the Roo.bootstrap object from renderder
6971 renderCellObject : function(r)
6975 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6977 var t = r.cfg.render(r.container);
6980 Roo.each(r.cfg.cn, function(c){
6982 container: t.getChildContainer(),
6985 _this.renderCellObject(child);
6990 getRowIndex : function(row)
6994 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7005 * Returns the grid's underlying element = used by panel.Grid
7006 * @return {Element} The element
7008 getGridEl : function(){
7012 * Forces a resize - used by panel.Grid
7013 * @return {Element} The element
7015 autoSize : function()
7017 //var ctr = Roo.get(this.container.dom.parentElement);
7018 var ctr = Roo.get(this.el.dom);
7020 var thd = this.getGridEl().select('thead',true).first();
7021 var tbd = this.getGridEl().select('tbody', true).first();
7022 var tfd = this.getGridEl().select('tfoot', true).first();
7024 var cw = ctr.getWidth();
7028 tbd.setSize(ctr.getWidth(),
7029 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7031 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7034 cw = Math.max(cw, this.totalWidth);
7035 this.getGridEl().select('tr',true).setWidth(cw);
7036 // resize 'expandable coloumn?
7038 return; // we doe not have a view in this design..
7041 onBodyScroll: function()
7043 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7045 this.mainHead.setStyle({
7046 'position' : 'relative',
7047 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7053 var scrollHeight = this.mainBody.dom.scrollHeight;
7055 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7057 var height = this.mainBody.getHeight();
7059 if(scrollHeight - height == scrollTop) {
7061 var total = this.ds.getTotalCount();
7063 if(this.footer.cursor + this.footer.pageSize < total){
7065 this.footer.ds.load({
7067 start : this.footer.cursor + this.footer.pageSize,
7068 limit : this.footer.pageSize
7078 onHeaderChange : function()
7080 var header = this.renderHeader();
7081 var table = this.el.select('table', true).first();
7083 this.mainHead.remove();
7084 this.mainHead = table.createChild(header, this.mainBody, false);
7087 onHiddenChange : function(colModel, colIndex, hidden)
7089 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7090 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7092 this.CSS.updateRule(thSelector, "display", "");
7093 this.CSS.updateRule(tdSelector, "display", "");
7096 this.CSS.updateRule(thSelector, "display", "none");
7097 this.CSS.updateRule(tdSelector, "display", "none");
7100 this.onHeaderChange();
7104 setColumnWidth: function(col_index, width)
7106 // width = "md-2 xs-2..."
7107 if(!this.colModel.config[col_index]) {
7111 var w = width.split(" ");
7113 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7115 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7118 for(var j = 0; w.length; j++) {
7122 var size_cls = w[j].split("-");
7124 if(!Number.isInteger(size_cls[1] * 1)) {
7128 if(!this.colModel.config[col_index][size_cls[0]]) {
7132 h_row[0].classList.replace(
7133 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7134 "col-"+size_cls[0]+"-"+size_cls[1]
7137 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7140 for(var i = 0; i < rows.length; i++) {
7142 for(var j = 0; w.length; j++) {
7144 var size_cls = w[j].split("-");
7146 if(!Number.isInteger(size_cls[1] * 1)) {
7150 if(!this.colModel.config[col_index][size_cls[0]]) {
7154 rows[i].classList.replace(
7155 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7156 "col-"+size_cls[0]+"-"+size_cls[1]
7173 * @class Roo.bootstrap.TableCell
7174 * @extends Roo.bootstrap.Component
7175 * Bootstrap TableCell class
7176 * @cfg {String} html cell contain text
7177 * @cfg {String} cls cell class
7178 * @cfg {String} tag cell tag (td|th) default td
7179 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7180 * @cfg {String} align Aligns the content in a cell
7181 * @cfg {String} axis Categorizes cells
7182 * @cfg {String} bgcolor Specifies the background color of a cell
7183 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7184 * @cfg {Number} colspan Specifies the number of columns a cell should span
7185 * @cfg {String} headers Specifies one or more header cells a cell is related to
7186 * @cfg {Number} height Sets the height of a cell
7187 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7188 * @cfg {Number} rowspan Sets the number of rows a cell should span
7189 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7190 * @cfg {String} valign Vertical aligns the content in a cell
7191 * @cfg {Number} width Specifies the width of a cell
7194 * Create a new TableCell
7195 * @param {Object} config The config object
7198 Roo.bootstrap.TableCell = function(config){
7199 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7202 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7222 getAutoCreate : function(){
7223 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7243 cfg.align=this.align
7249 cfg.bgcolor=this.bgcolor
7252 cfg.charoff=this.charoff
7255 cfg.colspan=this.colspan
7258 cfg.headers=this.headers
7261 cfg.height=this.height
7264 cfg.nowrap=this.nowrap
7267 cfg.rowspan=this.rowspan
7270 cfg.scope=this.scope
7273 cfg.valign=this.valign
7276 cfg.width=this.width
7295 * @class Roo.bootstrap.TableRow
7296 * @extends Roo.bootstrap.Component
7297 * Bootstrap TableRow class
7298 * @cfg {String} cls row class
7299 * @cfg {String} align Aligns the content in a table row
7300 * @cfg {String} bgcolor Specifies a background color for a table row
7301 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7302 * @cfg {String} valign Vertical aligns the content in a table row
7305 * Create a new TableRow
7306 * @param {Object} config The config object
7309 Roo.bootstrap.TableRow = function(config){
7310 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7313 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7321 getAutoCreate : function(){
7322 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7332 cfg.align = this.align;
7335 cfg.bgcolor = this.bgcolor;
7338 cfg.charoff = this.charoff;
7341 cfg.valign = this.valign;
7359 * @class Roo.bootstrap.TableBody
7360 * @extends Roo.bootstrap.Component
7361 * Bootstrap TableBody class
7362 * @cfg {String} cls element class
7363 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7364 * @cfg {String} align Aligns the content inside the element
7365 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7366 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7369 * Create a new TableBody
7370 * @param {Object} config The config object
7373 Roo.bootstrap.TableBody = function(config){
7374 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7377 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7385 getAutoCreate : function(){
7386 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7400 cfg.align = this.align;
7403 cfg.charoff = this.charoff;
7406 cfg.valign = this.valign;
7413 // initEvents : function()
7420 // this.store = Roo.factory(this.store, Roo.data);
7421 // this.store.on('load', this.onLoad, this);
7423 // this.store.load();
7427 // onLoad: function ()
7429 // this.fireEvent('load', this);
7439 * Ext JS Library 1.1.1
7440 * Copyright(c) 2006-2007, Ext JS, LLC.
7442 * Originally Released Under LGPL - original licence link has changed is not relivant.
7445 * <script type="text/javascript">
7448 // as we use this in bootstrap.
7449 Roo.namespace('Roo.form');
7451 * @class Roo.form.Action
7452 * Internal Class used to handle form actions
7454 * @param {Roo.form.BasicForm} el The form element or its id
7455 * @param {Object} config Configuration options
7460 // define the action interface
7461 Roo.form.Action = function(form, options){
7463 this.options = options || {};
7466 * Client Validation Failed
7469 Roo.form.Action.CLIENT_INVALID = 'client';
7471 * Server Validation Failed
7474 Roo.form.Action.SERVER_INVALID = 'server';
7476 * Connect to Server Failed
7479 Roo.form.Action.CONNECT_FAILURE = 'connect';
7481 * Reading Data from Server Failed
7484 Roo.form.Action.LOAD_FAILURE = 'load';
7486 Roo.form.Action.prototype = {
7488 failureType : undefined,
7489 response : undefined,
7493 run : function(options){
7498 success : function(response){
7503 handleResponse : function(response){
7507 // default connection failure
7508 failure : function(response){
7510 this.response = response;
7511 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7512 this.form.afterAction(this, false);
7515 processResponse : function(response){
7516 this.response = response;
7517 if(!response.responseText){
7520 this.result = this.handleResponse(response);
7524 // utility functions used internally
7525 getUrl : function(appendParams){
7526 var url = this.options.url || this.form.url || this.form.el.dom.action;
7528 var p = this.getParams();
7530 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7536 getMethod : function(){
7537 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7540 getParams : function(){
7541 var bp = this.form.baseParams;
7542 var p = this.options.params;
7544 if(typeof p == "object"){
7545 p = Roo.urlEncode(Roo.applyIf(p, bp));
7546 }else if(typeof p == 'string' && bp){
7547 p += '&' + Roo.urlEncode(bp);
7550 p = Roo.urlEncode(bp);
7555 createCallback : function(){
7557 success: this.success,
7558 failure: this.failure,
7560 timeout: (this.form.timeout*1000),
7561 upload: this.form.fileUpload ? this.success : undefined
7566 Roo.form.Action.Submit = function(form, options){
7567 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7570 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7573 haveProgress : false,
7574 uploadComplete : false,
7576 // uploadProgress indicator.
7577 uploadProgress : function()
7579 if (!this.form.progressUrl) {
7583 if (!this.haveProgress) {
7584 Roo.MessageBox.progress("Uploading", "Uploading");
7586 if (this.uploadComplete) {
7587 Roo.MessageBox.hide();
7591 this.haveProgress = true;
7593 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7595 var c = new Roo.data.Connection();
7597 url : this.form.progressUrl,
7602 success : function(req){
7603 //console.log(data);
7607 rdata = Roo.decode(req.responseText)
7609 Roo.log("Invalid data from server..");
7613 if (!rdata || !rdata.success) {
7615 Roo.MessageBox.alert(Roo.encode(rdata));
7618 var data = rdata.data;
7620 if (this.uploadComplete) {
7621 Roo.MessageBox.hide();
7626 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7627 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7630 this.uploadProgress.defer(2000,this);
7633 failure: function(data) {
7634 Roo.log('progress url failed ');
7645 // run get Values on the form, so it syncs any secondary forms.
7646 this.form.getValues();
7648 var o = this.options;
7649 var method = this.getMethod();
7650 var isPost = method == 'POST';
7651 if(o.clientValidation === false || this.form.isValid()){
7653 if (this.form.progressUrl) {
7654 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7655 (new Date() * 1) + '' + Math.random());
7660 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7661 form:this.form.el.dom,
7662 url:this.getUrl(!isPost),
7664 params:isPost ? this.getParams() : null,
7665 isUpload: this.form.fileUpload
7668 this.uploadProgress();
7670 }else if (o.clientValidation !== false){ // client validation failed
7671 this.failureType = Roo.form.Action.CLIENT_INVALID;
7672 this.form.afterAction(this, false);
7676 success : function(response)
7678 this.uploadComplete= true;
7679 if (this.haveProgress) {
7680 Roo.MessageBox.hide();
7684 var result = this.processResponse(response);
7685 if(result === true || result.success){
7686 this.form.afterAction(this, true);
7690 this.form.markInvalid(result.errors);
7691 this.failureType = Roo.form.Action.SERVER_INVALID;
7693 this.form.afterAction(this, false);
7695 failure : function(response)
7697 this.uploadComplete= true;
7698 if (this.haveProgress) {
7699 Roo.MessageBox.hide();
7702 this.response = response;
7703 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7704 this.form.afterAction(this, false);
7707 handleResponse : function(response){
7708 if(this.form.errorReader){
7709 var rs = this.form.errorReader.read(response);
7712 for(var i = 0, len = rs.records.length; i < len; i++) {
7713 var r = rs.records[i];
7717 if(errors.length < 1){
7721 success : rs.success,
7727 ret = Roo.decode(response.responseText);
7731 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7741 Roo.form.Action.Load = function(form, options){
7742 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7743 this.reader = this.form.reader;
7746 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7751 Roo.Ajax.request(Roo.apply(
7752 this.createCallback(), {
7753 method:this.getMethod(),
7754 url:this.getUrl(false),
7755 params:this.getParams()
7759 success : function(response){
7761 var result = this.processResponse(response);
7762 if(result === true || !result.success || !result.data){
7763 this.failureType = Roo.form.Action.LOAD_FAILURE;
7764 this.form.afterAction(this, false);
7767 this.form.clearInvalid();
7768 this.form.setValues(result.data);
7769 this.form.afterAction(this, true);
7772 handleResponse : function(response){
7773 if(this.form.reader){
7774 var rs = this.form.reader.read(response);
7775 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7777 success : rs.success,
7781 return Roo.decode(response.responseText);
7785 Roo.form.Action.ACTION_TYPES = {
7786 'load' : Roo.form.Action.Load,
7787 'submit' : Roo.form.Action.Submit
7796 * @class Roo.bootstrap.Form
7797 * @extends Roo.bootstrap.Component
7798 * Bootstrap Form class
7799 * @cfg {String} method GET | POST (default POST)
7800 * @cfg {String} labelAlign top | left (default top)
7801 * @cfg {String} align left | right - for navbars
7802 * @cfg {Boolean} loadMask load mask when submit (default true)
7807 * @param {Object} config The config object
7811 Roo.bootstrap.Form = function(config){
7813 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7815 Roo.bootstrap.Form.popover.apply();
7819 * @event clientvalidation
7820 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7821 * @param {Form} this
7822 * @param {Boolean} valid true if the form has passed client-side validation
7824 clientvalidation: true,
7826 * @event beforeaction
7827 * Fires before any action is performed. Return false to cancel the action.
7828 * @param {Form} this
7829 * @param {Action} action The action to be performed
7833 * @event actionfailed
7834 * Fires when an action fails.
7835 * @param {Form} this
7836 * @param {Action} action The action that failed
7838 actionfailed : true,
7840 * @event actioncomplete
7841 * Fires when an action is completed.
7842 * @param {Form} this
7843 * @param {Action} action The action that completed
7845 actioncomplete : true
7849 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7852 * @cfg {String} method
7853 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7858 * The URL to use for form actions if one isn't supplied in the action options.
7861 * @cfg {Boolean} fileUpload
7862 * Set to true if this form is a file upload.
7866 * @cfg {Object} baseParams
7867 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7871 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7875 * @cfg {Sting} align (left|right) for navbar forms
7880 activeAction : null,
7883 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7884 * element by passing it or its id or mask the form itself by passing in true.
7887 waitMsgTarget : false,
7892 * @cfg {Boolean} errorMask (true|false) default false
7897 * @cfg {Number} maskOffset Default 100
7902 * @cfg {Boolean} maskBody
7906 getAutoCreate : function(){
7910 method : this.method || 'POST',
7911 id : this.id || Roo.id(),
7914 if (this.parent().xtype.match(/^Nav/)) {
7915 cfg.cls = 'navbar-form navbar-' + this.align;
7919 if (this.labelAlign == 'left' ) {
7920 cfg.cls += ' form-horizontal';
7926 initEvents : function()
7928 this.el.on('submit', this.onSubmit, this);
7929 // this was added as random key presses on the form where triggering form submit.
7930 this.el.on('keypress', function(e) {
7931 if (e.getCharCode() != 13) {
7934 // we might need to allow it for textareas.. and some other items.
7935 // check e.getTarget().
7937 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7941 Roo.log("keypress blocked");
7949 onSubmit : function(e){
7954 * Returns true if client-side validation on the form is successful.
7957 isValid : function(){
7958 var items = this.getItems();
7962 items.each(function(f){
7968 Roo.log('invalid field: ' + f.name);
7972 if(!target && f.el.isVisible(true)){
7978 if(this.errorMask && !valid){
7979 Roo.bootstrap.Form.popover.mask(this, target);
7986 * Returns true if any fields in this form have changed since their original load.
7989 isDirty : function(){
7991 var items = this.getItems();
7992 items.each(function(f){
8002 * Performs a predefined action (submit or load) or custom actions you define on this form.
8003 * @param {String} actionName The name of the action type
8004 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8005 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8006 * accept other config options):
8008 Property Type Description
8009 ---------------- --------------- ----------------------------------------------------------------------------------
8010 url String The url for the action (defaults to the form's url)
8011 method String The form method to use (defaults to the form's method, or POST if not defined)
8012 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8013 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8014 validate the form on the client (defaults to false)
8016 * @return {BasicForm} this
8018 doAction : function(action, options){
8019 if(typeof action == 'string'){
8020 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8022 if(this.fireEvent('beforeaction', this, action) !== false){
8023 this.beforeAction(action);
8024 action.run.defer(100, action);
8030 beforeAction : function(action){
8031 var o = action.options;
8036 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8038 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8041 // not really supported yet.. ??
8043 //if(this.waitMsgTarget === true){
8044 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8045 //}else if(this.waitMsgTarget){
8046 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8047 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8049 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8055 afterAction : function(action, success){
8056 this.activeAction = null;
8057 var o = action.options;
8062 Roo.get(document.body).unmask();
8068 //if(this.waitMsgTarget === true){
8069 // this.el.unmask();
8070 //}else if(this.waitMsgTarget){
8071 // this.waitMsgTarget.unmask();
8073 // Roo.MessageBox.updateProgress(1);
8074 // Roo.MessageBox.hide();
8081 Roo.callback(o.success, o.scope, [this, action]);
8082 this.fireEvent('actioncomplete', this, action);
8086 // failure condition..
8087 // we have a scenario where updates need confirming.
8088 // eg. if a locking scenario exists..
8089 // we look for { errors : { needs_confirm : true }} in the response.
8091 (typeof(action.result) != 'undefined') &&
8092 (typeof(action.result.errors) != 'undefined') &&
8093 (typeof(action.result.errors.needs_confirm) != 'undefined')
8096 Roo.log("not supported yet");
8099 Roo.MessageBox.confirm(
8100 "Change requires confirmation",
8101 action.result.errorMsg,
8106 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8116 Roo.callback(o.failure, o.scope, [this, action]);
8117 // show an error message if no failed handler is set..
8118 if (!this.hasListener('actionfailed')) {
8119 Roo.log("need to add dialog support");
8121 Roo.MessageBox.alert("Error",
8122 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8123 action.result.errorMsg :
8124 "Saving Failed, please check your entries or try again"
8129 this.fireEvent('actionfailed', this, action);
8134 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8135 * @param {String} id The value to search for
8138 findField : function(id){
8139 var items = this.getItems();
8140 var field = items.get(id);
8142 items.each(function(f){
8143 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8150 return field || null;
8153 * Mark fields in this form invalid in bulk.
8154 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8155 * @return {BasicForm} this
8157 markInvalid : function(errors){
8158 if(errors instanceof Array){
8159 for(var i = 0, len = errors.length; i < len; i++){
8160 var fieldError = errors[i];
8161 var f = this.findField(fieldError.id);
8163 f.markInvalid(fieldError.msg);
8169 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8170 field.markInvalid(errors[id]);
8174 //Roo.each(this.childForms || [], function (f) {
8175 // f.markInvalid(errors);
8182 * Set values for fields in this form in bulk.
8183 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8184 * @return {BasicForm} this
8186 setValues : function(values){
8187 if(values instanceof Array){ // array of objects
8188 for(var i = 0, len = values.length; i < len; i++){
8190 var f = this.findField(v.id);
8192 f.setValue(v.value);
8193 if(this.trackResetOnLoad){
8194 f.originalValue = f.getValue();
8198 }else{ // object hash
8201 if(typeof values[id] != 'function' && (field = this.findField(id))){
8203 if (field.setFromData &&
8205 field.displayField &&
8206 // combos' with local stores can
8207 // be queried via setValue()
8208 // to set their value..
8209 (field.store && !field.store.isLocal)
8213 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8214 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8215 field.setFromData(sd);
8217 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8219 field.setFromData(values);
8222 field.setValue(values[id]);
8226 if(this.trackResetOnLoad){
8227 field.originalValue = field.getValue();
8233 //Roo.each(this.childForms || [], function (f) {
8234 // f.setValues(values);
8241 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8242 * they are returned as an array.
8243 * @param {Boolean} asString
8246 getValues : function(asString){
8247 //if (this.childForms) {
8248 // copy values from the child forms
8249 // Roo.each(this.childForms, function (f) {
8250 // this.setValues(f.getValues());
8256 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8257 if(asString === true){
8260 return Roo.urlDecode(fs);
8264 * Returns the fields in this form as an object with key/value pairs.
8265 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8268 getFieldValues : function(with_hidden)
8270 var items = this.getItems();
8272 items.each(function(f){
8278 var v = f.getValue();
8280 if (f.inputType =='radio') {
8281 if (typeof(ret[f.getName()]) == 'undefined') {
8282 ret[f.getName()] = ''; // empty..
8285 if (!f.el.dom.checked) {
8293 if(f.xtype == 'MoneyField'){
8294 ret[f.currencyName] = f.getCurrency();
8297 // not sure if this supported any more..
8298 if ((typeof(v) == 'object') && f.getRawValue) {
8299 v = f.getRawValue() ; // dates..
8301 // combo boxes where name != hiddenName...
8302 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8303 ret[f.name] = f.getRawValue();
8305 ret[f.getName()] = v;
8312 * Clears all invalid messages in this form.
8313 * @return {BasicForm} this
8315 clearInvalid : function(){
8316 var items = this.getItems();
8318 items.each(function(f){
8327 * @return {BasicForm} this
8330 var items = this.getItems();
8331 items.each(function(f){
8335 Roo.each(this.childForms || [], function (f) {
8343 getItems : function()
8345 var r=new Roo.util.MixedCollection(false, function(o){
8346 return o.id || (o.id = Roo.id());
8348 var iter = function(el) {
8355 Roo.each(el.items,function(e) {
8364 hideFields : function(items)
8366 Roo.each(items, function(i){
8368 var f = this.findField(i);
8379 showFields : function(items)
8381 Roo.each(items, function(i){
8383 var f = this.findField(i);
8396 Roo.apply(Roo.bootstrap.Form, {
8423 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8424 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8425 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8426 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8429 this.maskEl.top.enableDisplayMode("block");
8430 this.maskEl.left.enableDisplayMode("block");
8431 this.maskEl.bottom.enableDisplayMode("block");
8432 this.maskEl.right.enableDisplayMode("block");
8434 this.toolTip = new Roo.bootstrap.Tooltip({
8435 cls : 'roo-form-error-popover',
8437 'left' : ['r-l', [-2,0], 'right'],
8438 'right' : ['l-r', [2,0], 'left'],
8439 'bottom' : ['tl-bl', [0,2], 'top'],
8440 'top' : [ 'bl-tl', [0,-2], 'bottom']
8444 this.toolTip.render(Roo.get(document.body));
8446 this.toolTip.el.enableDisplayMode("block");
8448 Roo.get(document.body).on('click', function(){
8452 Roo.get(document.body).on('touchstart', function(){
8456 this.isApplied = true
8459 mask : function(form, target)
8463 this.target = target;
8465 if(!this.form.errorMask || !target.el){
8469 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8471 Roo.log(scrollable);
8473 var ot = this.target.el.calcOffsetsTo(scrollable);
8475 var scrollTo = ot[1] - this.form.maskOffset;
8477 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8479 scrollable.scrollTo('top', scrollTo);
8481 var box = this.target.el.getBox();
8483 var zIndex = Roo.bootstrap.Modal.zIndex++;
8486 this.maskEl.top.setStyle('position', 'absolute');
8487 this.maskEl.top.setStyle('z-index', zIndex);
8488 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8489 this.maskEl.top.setLeft(0);
8490 this.maskEl.top.setTop(0);
8491 this.maskEl.top.show();
8493 this.maskEl.left.setStyle('position', 'absolute');
8494 this.maskEl.left.setStyle('z-index', zIndex);
8495 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8496 this.maskEl.left.setLeft(0);
8497 this.maskEl.left.setTop(box.y - this.padding);
8498 this.maskEl.left.show();
8500 this.maskEl.bottom.setStyle('position', 'absolute');
8501 this.maskEl.bottom.setStyle('z-index', zIndex);
8502 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8503 this.maskEl.bottom.setLeft(0);
8504 this.maskEl.bottom.setTop(box.bottom + this.padding);
8505 this.maskEl.bottom.show();
8507 this.maskEl.right.setStyle('position', 'absolute');
8508 this.maskEl.right.setStyle('z-index', zIndex);
8509 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8510 this.maskEl.right.setLeft(box.right + this.padding);
8511 this.maskEl.right.setTop(box.y - this.padding);
8512 this.maskEl.right.show();
8514 this.toolTip.bindEl = this.target.el;
8516 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8518 var tip = this.target.blankText;
8520 if(this.target.getValue() !== '' ) {
8522 if (this.target.invalidText.length) {
8523 tip = this.target.invalidText;
8524 } else if (this.target.regexText.length){
8525 tip = this.target.regexText;
8529 this.toolTip.show(tip);
8531 this.intervalID = window.setInterval(function() {
8532 Roo.bootstrap.Form.popover.unmask();
8535 window.onwheel = function(){ return false;};
8537 (function(){ this.isMasked = true; }).defer(500, this);
8543 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8547 this.maskEl.top.setStyle('position', 'absolute');
8548 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8549 this.maskEl.top.hide();
8551 this.maskEl.left.setStyle('position', 'absolute');
8552 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8553 this.maskEl.left.hide();
8555 this.maskEl.bottom.setStyle('position', 'absolute');
8556 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8557 this.maskEl.bottom.hide();
8559 this.maskEl.right.setStyle('position', 'absolute');
8560 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8561 this.maskEl.right.hide();
8563 this.toolTip.hide();
8565 this.toolTip.el.hide();
8567 window.onwheel = function(){ return true;};
8569 if(this.intervalID){
8570 window.clearInterval(this.intervalID);
8571 this.intervalID = false;
8574 this.isMasked = false;
8584 * Ext JS Library 1.1.1
8585 * Copyright(c) 2006-2007, Ext JS, LLC.
8587 * Originally Released Under LGPL - original licence link has changed is not relivant.
8590 * <script type="text/javascript">
8593 * @class Roo.form.VTypes
8594 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8597 Roo.form.VTypes = function(){
8598 // closure these in so they are only created once.
8599 var alpha = /^[a-zA-Z_]+$/;
8600 var alphanum = /^[a-zA-Z0-9_]+$/;
8601 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8602 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8604 // All these messages and functions are configurable
8607 * The function used to validate email addresses
8608 * @param {String} value The email address
8610 'email' : function(v){
8611 return email.test(v);
8614 * The error text to display when the email validation function returns false
8617 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8619 * The keystroke filter mask to be applied on email input
8622 'emailMask' : /[a-z0-9_\.\-@]/i,
8625 * The function used to validate URLs
8626 * @param {String} value The URL
8628 'url' : function(v){
8632 * The error text to display when the url validation function returns false
8635 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8638 * The function used to validate alpha values
8639 * @param {String} value The value
8641 'alpha' : function(v){
8642 return alpha.test(v);
8645 * The error text to display when the alpha validation function returns false
8648 'alphaText' : 'This field should only contain letters and _',
8650 * The keystroke filter mask to be applied on alpha input
8653 'alphaMask' : /[a-z_]/i,
8656 * The function used to validate alphanumeric values
8657 * @param {String} value The value
8659 'alphanum' : function(v){
8660 return alphanum.test(v);
8663 * The error text to display when the alphanumeric validation function returns false
8666 'alphanumText' : 'This field should only contain letters, numbers and _',
8668 * The keystroke filter mask to be applied on alphanumeric input
8671 'alphanumMask' : /[a-z0-9_]/i
8681 * @class Roo.bootstrap.Input
8682 * @extends Roo.bootstrap.Component
8683 * Bootstrap Input class
8684 * @cfg {Boolean} disabled is it disabled
8685 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8686 * @cfg {String} name name of the input
8687 * @cfg {string} fieldLabel - the label associated
8688 * @cfg {string} placeholder - placeholder to put in text.
8689 * @cfg {string} before - input group add on before
8690 * @cfg {string} after - input group add on after
8691 * @cfg {string} size - (lg|sm) or leave empty..
8692 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8693 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8694 * @cfg {Number} md colspan out of 12 for computer-sized screens
8695 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8696 * @cfg {string} value default value of the input
8697 * @cfg {Number} labelWidth set the width of label
8698 * @cfg {Number} labellg set the width of label (1-12)
8699 * @cfg {Number} labelmd set the width of label (1-12)
8700 * @cfg {Number} labelsm set the width of label (1-12)
8701 * @cfg {Number} labelxs set the width of label (1-12)
8702 * @cfg {String} labelAlign (top|left)
8703 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8704 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8705 * @cfg {String} indicatorpos (left|right) default left
8706 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8707 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8709 * @cfg {String} align (left|center|right) Default left
8710 * @cfg {Boolean} forceFeedback (true|false) Default false
8713 * Create a new Input
8714 * @param {Object} config The config object
8717 Roo.bootstrap.Input = function(config){
8719 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8724 * Fires when this field receives input focus.
8725 * @param {Roo.form.Field} this
8730 * Fires when this field loses input focus.
8731 * @param {Roo.form.Field} this
8736 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8737 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8738 * @param {Roo.form.Field} this
8739 * @param {Roo.EventObject} e The event object
8744 * Fires just before the field blurs if the field value has changed.
8745 * @param {Roo.form.Field} this
8746 * @param {Mixed} newValue The new value
8747 * @param {Mixed} oldValue The original value
8752 * Fires after the field has been marked as invalid.
8753 * @param {Roo.form.Field} this
8754 * @param {String} msg The validation message
8759 * Fires after the field has been validated with no errors.
8760 * @param {Roo.form.Field} this
8765 * Fires after the key up
8766 * @param {Roo.form.Field} this
8767 * @param {Roo.EventObject} e The event Object
8773 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8775 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8776 automatic validation (defaults to "keyup").
8778 validationEvent : "keyup",
8780 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8782 validateOnBlur : true,
8784 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8786 validationDelay : 250,
8788 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8790 focusClass : "x-form-focus", // not needed???
8794 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8796 invalidClass : "has-warning",
8799 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8801 validClass : "has-success",
8804 * @cfg {Boolean} hasFeedback (true|false) default true
8809 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8811 invalidFeedbackClass : "glyphicon-warning-sign",
8814 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8816 validFeedbackClass : "glyphicon-ok",
8819 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8821 selectOnFocus : false,
8824 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8828 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8833 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8835 disableKeyFilter : false,
8838 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8842 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8846 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8848 blankText : "Please complete this mandatory field",
8851 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8855 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8857 maxLength : Number.MAX_VALUE,
8859 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8861 minLengthText : "The minimum length for this field is {0}",
8863 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8865 maxLengthText : "The maximum length for this field is {0}",
8869 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8870 * If available, this function will be called only after the basic validators all return true, and will be passed the
8871 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8875 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8876 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8877 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8881 * @cfg {String} regexText -- Depricated - use Invalid Text
8886 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8892 autocomplete: false,
8911 formatedValue : false,
8912 forceFeedback : false,
8914 indicatorpos : 'left',
8924 parentLabelAlign : function()
8927 while (parent.parent()) {
8928 parent = parent.parent();
8929 if (typeof(parent.labelAlign) !='undefined') {
8930 return parent.labelAlign;
8937 getAutoCreate : function()
8939 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8945 if(this.inputType != 'hidden'){
8946 cfg.cls = 'form-group' //input-group
8952 type : this.inputType,
8954 cls : 'form-control',
8955 placeholder : this.placeholder || '',
8956 autocomplete : this.autocomplete || 'new-password'
8959 if(this.capture.length){
8960 input.capture = this.capture;
8963 if(this.accept.length){
8964 input.accept = this.accept + "/*";
8968 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8971 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8972 input.maxLength = this.maxLength;
8975 if (this.disabled) {
8976 input.disabled=true;
8979 if (this.readOnly) {
8980 input.readonly=true;
8984 input.name = this.name;
8988 input.cls += ' input-' + this.size;
8992 ['xs','sm','md','lg'].map(function(size){
8993 if (settings[size]) {
8994 cfg.cls += ' col-' + size + '-' + settings[size];
8998 var inputblock = input;
9002 cls: 'glyphicon form-control-feedback'
9005 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9008 cls : 'has-feedback',
9016 if (this.before || this.after) {
9019 cls : 'input-group',
9023 if (this.before && typeof(this.before) == 'string') {
9025 inputblock.cn.push({
9027 cls : 'roo-input-before input-group-addon',
9031 if (this.before && typeof(this.before) == 'object') {
9032 this.before = Roo.factory(this.before);
9034 inputblock.cn.push({
9036 cls : 'roo-input-before input-group-' +
9037 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9041 inputblock.cn.push(input);
9043 if (this.after && typeof(this.after) == 'string') {
9044 inputblock.cn.push({
9046 cls : 'roo-input-after input-group-addon',
9050 if (this.after && typeof(this.after) == 'object') {
9051 this.after = Roo.factory(this.after);
9053 inputblock.cn.push({
9055 cls : 'roo-input-after input-group-' +
9056 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9060 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9061 inputblock.cls += ' has-feedback';
9062 inputblock.cn.push(feedback);
9066 if (align ==='left' && this.fieldLabel.length) {
9068 cfg.cls += ' roo-form-group-label-left';
9073 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9074 tooltip : 'This field is required'
9079 cls : 'control-label',
9080 html : this.fieldLabel
9091 var labelCfg = cfg.cn[1];
9092 var contentCfg = cfg.cn[2];
9094 if(this.indicatorpos == 'right'){
9099 cls : 'control-label',
9103 html : this.fieldLabel
9107 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9108 tooltip : 'This field is required'
9121 labelCfg = cfg.cn[0];
9122 contentCfg = cfg.cn[1];
9126 if(this.labelWidth > 12){
9127 labelCfg.style = "width: " + this.labelWidth + 'px';
9130 if(this.labelWidth < 13 && this.labelmd == 0){
9131 this.labelmd = this.labelWidth;
9134 if(this.labellg > 0){
9135 labelCfg.cls += ' col-lg-' + this.labellg;
9136 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9139 if(this.labelmd > 0){
9140 labelCfg.cls += ' col-md-' + this.labelmd;
9141 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9144 if(this.labelsm > 0){
9145 labelCfg.cls += ' col-sm-' + this.labelsm;
9146 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9149 if(this.labelxs > 0){
9150 labelCfg.cls += ' col-xs-' + this.labelxs;
9151 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9155 } else if ( this.fieldLabel.length) {
9160 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9161 tooltip : 'This field is required'
9165 //cls : 'input-group-addon',
9166 html : this.fieldLabel
9174 if(this.indicatorpos == 'right'){
9179 //cls : 'input-group-addon',
9180 html : this.fieldLabel
9185 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9186 tooltip : 'This field is required'
9206 if (this.parentType === 'Navbar' && this.parent().bar) {
9207 cfg.cls += ' navbar-form';
9210 if (this.parentType === 'NavGroup') {
9211 cfg.cls += ' navbar-form';
9219 * return the real input element.
9221 inputEl: function ()
9223 return this.el.select('input.form-control',true).first();
9226 tooltipEl : function()
9228 return this.inputEl();
9231 indicatorEl : function()
9233 var indicator = this.el.select('i.roo-required-indicator',true).first();
9243 setDisabled : function(v)
9245 var i = this.inputEl().dom;
9247 i.removeAttribute('disabled');
9251 i.setAttribute('disabled','true');
9253 initEvents : function()
9256 this.inputEl().on("keydown" , this.fireKey, this);
9257 this.inputEl().on("focus", this.onFocus, this);
9258 this.inputEl().on("blur", this.onBlur, this);
9260 this.inputEl().relayEvent('keyup', this);
9262 this.indicator = this.indicatorEl();
9265 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9268 // reference to original value for reset
9269 this.originalValue = this.getValue();
9270 //Roo.form.TextField.superclass.initEvents.call(this);
9271 if(this.validationEvent == 'keyup'){
9272 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9273 this.inputEl().on('keyup', this.filterValidation, this);
9275 else if(this.validationEvent !== false){
9276 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9279 if(this.selectOnFocus){
9280 this.on("focus", this.preFocus, this);
9283 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9284 this.inputEl().on("keypress", this.filterKeys, this);
9286 this.inputEl().relayEvent('keypress', this);
9289 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9290 this.el.on("click", this.autoSize, this);
9293 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9294 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9297 if (typeof(this.before) == 'object') {
9298 this.before.render(this.el.select('.roo-input-before',true).first());
9300 if (typeof(this.after) == 'object') {
9301 this.after.render(this.el.select('.roo-input-after',true).first());
9304 this.inputEl().on('change', this.onChange, this);
9307 filterValidation : function(e){
9308 if(!e.isNavKeyPress()){
9309 this.validationTask.delay(this.validationDelay);
9313 * Validates the field value
9314 * @return {Boolean} True if the value is valid, else false
9316 validate : function(){
9317 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9318 if(this.disabled || this.validateValue(this.getRawValue())){
9329 * Validates a value according to the field's validation rules and marks the field as invalid
9330 * if the validation fails
9331 * @param {Mixed} value The value to validate
9332 * @return {Boolean} True if the value is valid, else false
9334 validateValue : function(value)
9336 if(this.getVisibilityEl().hasClass('hidden')){
9340 if(value.length < 1) { // if it's blank
9341 if(this.allowBlank){
9347 if(value.length < this.minLength){
9350 if(value.length > this.maxLength){
9354 var vt = Roo.form.VTypes;
9355 if(!vt[this.vtype](value, this)){
9359 if(typeof this.validator == "function"){
9360 var msg = this.validator(value);
9364 if (typeof(msg) == 'string') {
9365 this.invalidText = msg;
9369 if(this.regex && !this.regex.test(value)){
9377 fireKey : function(e){
9378 //Roo.log('field ' + e.getKey());
9379 if(e.isNavKeyPress()){
9380 this.fireEvent("specialkey", this, e);
9383 focus : function (selectText){
9385 this.inputEl().focus();
9386 if(selectText === true){
9387 this.inputEl().dom.select();
9393 onFocus : function(){
9394 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9395 // this.el.addClass(this.focusClass);
9398 this.hasFocus = true;
9399 this.startValue = this.getValue();
9400 this.fireEvent("focus", this);
9404 beforeBlur : Roo.emptyFn,
9408 onBlur : function(){
9410 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9411 //this.el.removeClass(this.focusClass);
9413 this.hasFocus = false;
9414 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9417 var v = this.getValue();
9418 if(String(v) !== String(this.startValue)){
9419 this.fireEvent('change', this, v, this.startValue);
9421 this.fireEvent("blur", this);
9424 onChange : function(e)
9426 var v = this.getValue();
9427 if(String(v) !== String(this.startValue)){
9428 this.fireEvent('change', this, v, this.startValue);
9434 * Resets the current field value to the originally loaded value and clears any validation messages
9437 this.setValue(this.originalValue);
9441 * Returns the name of the field
9442 * @return {Mixed} name The name field
9444 getName: function(){
9448 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9449 * @return {Mixed} value The field value
9451 getValue : function(){
9453 var v = this.inputEl().getValue();
9458 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9459 * @return {Mixed} value The field value
9461 getRawValue : function(){
9462 var v = this.inputEl().getValue();
9468 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9469 * @param {Mixed} value The value to set
9471 setRawValue : function(v){
9472 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9475 selectText : function(start, end){
9476 var v = this.getRawValue();
9478 start = start === undefined ? 0 : start;
9479 end = end === undefined ? v.length : end;
9480 var d = this.inputEl().dom;
9481 if(d.setSelectionRange){
9482 d.setSelectionRange(start, end);
9483 }else if(d.createTextRange){
9484 var range = d.createTextRange();
9485 range.moveStart("character", start);
9486 range.moveEnd("character", v.length-end);
9493 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9494 * @param {Mixed} value The value to set
9496 setValue : function(v){
9499 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9505 processValue : function(value){
9506 if(this.stripCharsRe){
9507 var newValue = value.replace(this.stripCharsRe, '');
9508 if(newValue !== value){
9509 this.setRawValue(newValue);
9516 preFocus : function(){
9518 if(this.selectOnFocus){
9519 this.inputEl().dom.select();
9522 filterKeys : function(e){
9524 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9527 var c = e.getCharCode(), cc = String.fromCharCode(c);
9528 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9531 if(!this.maskRe.test(cc)){
9536 * Clear any invalid styles/messages for this field
9538 clearInvalid : function(){
9540 if(!this.el || this.preventMark){ // not rendered
9545 this.el.removeClass(this.invalidClass);
9547 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9549 var feedback = this.el.select('.form-control-feedback', true).first();
9552 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9558 this.indicator.removeClass('visible');
9559 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9562 this.fireEvent('valid', this);
9566 * Mark this field as valid
9568 markValid : function()
9570 if(!this.el || this.preventMark){ // not rendered...
9574 this.el.removeClass([this.invalidClass, this.validClass]);
9576 var feedback = this.el.select('.form-control-feedback', true).first();
9579 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9583 this.indicator.removeClass('visible');
9584 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9591 if(this.allowBlank && !this.getRawValue().length){
9595 this.el.addClass(this.validClass);
9597 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9599 var feedback = this.el.select('.form-control-feedback', true).first();
9602 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9603 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9608 this.fireEvent('valid', this);
9612 * Mark this field as invalid
9613 * @param {String} msg The validation message
9615 markInvalid : function(msg)
9617 if(!this.el || this.preventMark){ // not rendered
9621 this.el.removeClass([this.invalidClass, this.validClass]);
9623 var feedback = this.el.select('.form-control-feedback', true).first();
9626 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9633 if(this.allowBlank && !this.getRawValue().length){
9638 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9639 this.indicator.addClass('visible');
9642 this.el.addClass(this.invalidClass);
9644 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9646 var feedback = this.el.select('.form-control-feedback', true).first();
9649 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9651 if(this.getValue().length || this.forceFeedback){
9652 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9659 this.fireEvent('invalid', this, msg);
9662 SafariOnKeyDown : function(event)
9664 // this is a workaround for a password hang bug on chrome/ webkit.
9665 if (this.inputEl().dom.type != 'password') {
9669 var isSelectAll = false;
9671 if(this.inputEl().dom.selectionEnd > 0){
9672 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9674 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9675 event.preventDefault();
9680 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9682 event.preventDefault();
9683 // this is very hacky as keydown always get's upper case.
9685 var cc = String.fromCharCode(event.getCharCode());
9686 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9690 adjustWidth : function(tag, w){
9691 tag = tag.toLowerCase();
9692 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9693 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9697 if(tag == 'textarea'){
9700 }else if(Roo.isOpera){
9704 if(tag == 'textarea'){
9712 setFieldLabel : function(v)
9719 var ar = this.el.select('label > span',true);
9721 if (ar.elements.length) {
9722 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9723 this.fieldLabel = v;
9727 var br = this.el.select('label',true);
9729 if(br.elements.length) {
9730 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9731 this.fieldLabel = v;
9735 Roo.log('Cannot Found any of label > span || label in input');
9739 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9740 this.fieldLabel = v;
9755 * @class Roo.bootstrap.TextArea
9756 * @extends Roo.bootstrap.Input
9757 * Bootstrap TextArea class
9758 * @cfg {Number} cols Specifies the visible width of a text area
9759 * @cfg {Number} rows Specifies the visible number of lines in a text area
9760 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9761 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9762 * @cfg {string} html text
9765 * Create a new TextArea
9766 * @param {Object} config The config object
9769 Roo.bootstrap.TextArea = function(config){
9770 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9774 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9784 getAutoCreate : function(){
9786 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9792 if(this.inputType != 'hidden'){
9793 cfg.cls = 'form-group' //input-group
9801 value : this.value || '',
9802 html: this.html || '',
9803 cls : 'form-control',
9804 placeholder : this.placeholder || ''
9808 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9809 input.maxLength = this.maxLength;
9813 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9817 input.cols = this.cols;
9820 if (this.readOnly) {
9821 input.readonly = true;
9825 input.name = this.name;
9829 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9833 ['xs','sm','md','lg'].map(function(size){
9834 if (settings[size]) {
9835 cfg.cls += ' col-' + size + '-' + settings[size];
9839 var inputblock = input;
9841 if(this.hasFeedback && !this.allowBlank){
9845 cls: 'glyphicon form-control-feedback'
9849 cls : 'has-feedback',
9858 if (this.before || this.after) {
9861 cls : 'input-group',
9865 inputblock.cn.push({
9867 cls : 'input-group-addon',
9872 inputblock.cn.push(input);
9874 if(this.hasFeedback && !this.allowBlank){
9875 inputblock.cls += ' has-feedback';
9876 inputblock.cn.push(feedback);
9880 inputblock.cn.push({
9882 cls : 'input-group-addon',
9889 if (align ==='left' && this.fieldLabel.length) {
9894 cls : 'control-label',
9895 html : this.fieldLabel
9906 if(this.labelWidth > 12){
9907 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9910 if(this.labelWidth < 13 && this.labelmd == 0){
9911 this.labelmd = this.labelWidth;
9914 if(this.labellg > 0){
9915 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9916 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9919 if(this.labelmd > 0){
9920 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9921 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9924 if(this.labelsm > 0){
9925 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9926 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9929 if(this.labelxs > 0){
9930 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9931 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9934 } else if ( this.fieldLabel.length) {
9939 //cls : 'input-group-addon',
9940 html : this.fieldLabel
9958 if (this.disabled) {
9959 input.disabled=true;
9966 * return the real textarea element.
9968 inputEl: function ()
9970 return this.el.select('textarea.form-control',true).first();
9974 * Clear any invalid styles/messages for this field
9976 clearInvalid : function()
9979 if(!this.el || this.preventMark){ // not rendered
9983 var label = this.el.select('label', true).first();
9984 var icon = this.el.select('i.fa-star', true).first();
9990 this.el.removeClass(this.invalidClass);
9992 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9994 var feedback = this.el.select('.form-control-feedback', true).first();
9997 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10002 this.fireEvent('valid', this);
10006 * Mark this field as valid
10008 markValid : function()
10010 if(!this.el || this.preventMark){ // not rendered
10014 this.el.removeClass([this.invalidClass, this.validClass]);
10016 var feedback = this.el.select('.form-control-feedback', true).first();
10019 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10022 if(this.disabled || this.allowBlank){
10026 var label = this.el.select('label', true).first();
10027 var icon = this.el.select('i.fa-star', true).first();
10033 this.el.addClass(this.validClass);
10035 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10037 var feedback = this.el.select('.form-control-feedback', true).first();
10040 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10041 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10046 this.fireEvent('valid', this);
10050 * Mark this field as invalid
10051 * @param {String} msg The validation message
10053 markInvalid : function(msg)
10055 if(!this.el || this.preventMark){ // not rendered
10059 this.el.removeClass([this.invalidClass, this.validClass]);
10061 var feedback = this.el.select('.form-control-feedback', true).first();
10064 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10067 if(this.disabled || this.allowBlank){
10071 var label = this.el.select('label', true).first();
10072 var icon = this.el.select('i.fa-star', true).first();
10074 if(!this.getValue().length && label && !icon){
10075 this.el.createChild({
10077 cls : 'text-danger fa fa-lg fa-star',
10078 tooltip : 'This field is required',
10079 style : 'margin-right:5px;'
10083 this.el.addClass(this.invalidClass);
10085 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10087 var feedback = this.el.select('.form-control-feedback', true).first();
10090 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10092 if(this.getValue().length || this.forceFeedback){
10093 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10100 this.fireEvent('invalid', this, msg);
10108 * trigger field - base class for combo..
10113 * @class Roo.bootstrap.TriggerField
10114 * @extends Roo.bootstrap.Input
10115 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10116 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10117 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10118 * for which you can provide a custom implementation. For example:
10120 var trigger = new Roo.bootstrap.TriggerField();
10121 trigger.onTriggerClick = myTriggerFn;
10122 trigger.applyTo('my-field');
10125 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10126 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10127 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10128 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10129 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10132 * Create a new TriggerField.
10133 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10134 * to the base TextField)
10136 Roo.bootstrap.TriggerField = function(config){
10137 this.mimicing = false;
10138 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10141 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10143 * @cfg {String} triggerClass A CSS class to apply to the trigger
10146 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10151 * @cfg {Boolean} removable (true|false) special filter default false
10155 /** @cfg {Boolean} grow @hide */
10156 /** @cfg {Number} growMin @hide */
10157 /** @cfg {Number} growMax @hide */
10163 autoSize: Roo.emptyFn,
10167 deferHeight : true,
10170 actionMode : 'wrap',
10175 getAutoCreate : function(){
10177 var align = this.labelAlign || this.parentLabelAlign();
10182 cls: 'form-group' //input-group
10189 type : this.inputType,
10190 cls : 'form-control',
10191 autocomplete: 'new-password',
10192 placeholder : this.placeholder || ''
10196 input.name = this.name;
10199 input.cls += ' input-' + this.size;
10202 if (this.disabled) {
10203 input.disabled=true;
10206 var inputblock = input;
10208 if(this.hasFeedback && !this.allowBlank){
10212 cls: 'glyphicon form-control-feedback'
10215 if(this.removable && !this.editable && !this.tickable){
10217 cls : 'has-feedback',
10223 cls : 'roo-combo-removable-btn close'
10230 cls : 'has-feedback',
10239 if(this.removable && !this.editable && !this.tickable){
10241 cls : 'roo-removable',
10247 cls : 'roo-combo-removable-btn close'
10254 if (this.before || this.after) {
10257 cls : 'input-group',
10261 inputblock.cn.push({
10263 cls : 'input-group-addon',
10268 inputblock.cn.push(input);
10270 if(this.hasFeedback && !this.allowBlank){
10271 inputblock.cls += ' has-feedback';
10272 inputblock.cn.push(feedback);
10276 inputblock.cn.push({
10278 cls : 'input-group-addon',
10291 cls: 'form-hidden-field'
10305 cls: 'form-hidden-field'
10309 cls: 'roo-select2-choices',
10313 cls: 'roo-select2-search-field',
10326 cls: 'roo-select2-container input-group',
10331 // cls: 'typeahead typeahead-long dropdown-menu',
10332 // style: 'display:none'
10337 if(!this.multiple && this.showToggleBtn){
10343 if (this.caret != false) {
10346 cls: 'fa fa-' + this.caret
10353 cls : 'input-group-addon btn dropdown-toggle',
10358 cls: 'combobox-clear',
10372 combobox.cls += ' roo-select2-container-multi';
10375 if (align ==='left' && this.fieldLabel.length) {
10377 cfg.cls += ' roo-form-group-label-left';
10382 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10383 tooltip : 'This field is required'
10388 cls : 'control-label',
10389 html : this.fieldLabel
10401 var labelCfg = cfg.cn[1];
10402 var contentCfg = cfg.cn[2];
10404 if(this.indicatorpos == 'right'){
10409 cls : 'control-label',
10413 html : this.fieldLabel
10417 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10418 tooltip : 'This field is required'
10431 labelCfg = cfg.cn[0];
10432 contentCfg = cfg.cn[1];
10435 if(this.labelWidth > 12){
10436 labelCfg.style = "width: " + this.labelWidth + 'px';
10439 if(this.labelWidth < 13 && this.labelmd == 0){
10440 this.labelmd = this.labelWidth;
10443 if(this.labellg > 0){
10444 labelCfg.cls += ' col-lg-' + this.labellg;
10445 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10448 if(this.labelmd > 0){
10449 labelCfg.cls += ' col-md-' + this.labelmd;
10450 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10453 if(this.labelsm > 0){
10454 labelCfg.cls += ' col-sm-' + this.labelsm;
10455 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10458 if(this.labelxs > 0){
10459 labelCfg.cls += ' col-xs-' + this.labelxs;
10460 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10463 } else if ( this.fieldLabel.length) {
10464 // Roo.log(" label");
10468 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10469 tooltip : 'This field is required'
10473 //cls : 'input-group-addon',
10474 html : this.fieldLabel
10482 if(this.indicatorpos == 'right'){
10490 html : this.fieldLabel
10494 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10495 tooltip : 'This field is required'
10508 // Roo.log(" no label && no align");
10515 ['xs','sm','md','lg'].map(function(size){
10516 if (settings[size]) {
10517 cfg.cls += ' col-' + size + '-' + settings[size];
10528 onResize : function(w, h){
10529 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10530 // if(typeof w == 'number'){
10531 // var x = w - this.trigger.getWidth();
10532 // this.inputEl().setWidth(this.adjustWidth('input', x));
10533 // this.trigger.setStyle('left', x+'px');
10538 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10541 getResizeEl : function(){
10542 return this.inputEl();
10546 getPositionEl : function(){
10547 return this.inputEl();
10551 alignErrorIcon : function(){
10552 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10556 initEvents : function(){
10560 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10561 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10562 if(!this.multiple && this.showToggleBtn){
10563 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10564 if(this.hideTrigger){
10565 this.trigger.setDisplayed(false);
10567 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10571 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10574 if(this.removable && !this.editable && !this.tickable){
10575 var close = this.closeTriggerEl();
10578 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10579 close.on('click', this.removeBtnClick, this, close);
10583 //this.trigger.addClassOnOver('x-form-trigger-over');
10584 //this.trigger.addClassOnClick('x-form-trigger-click');
10587 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10591 closeTriggerEl : function()
10593 var close = this.el.select('.roo-combo-removable-btn', true).first();
10594 return close ? close : false;
10597 removeBtnClick : function(e, h, el)
10599 e.preventDefault();
10601 if(this.fireEvent("remove", this) !== false){
10603 this.fireEvent("afterremove", this)
10607 createList : function()
10609 this.list = Roo.get(document.body).createChild({
10611 cls: 'typeahead typeahead-long dropdown-menu',
10612 style: 'display:none'
10615 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10620 initTrigger : function(){
10625 onDestroy : function(){
10627 this.trigger.removeAllListeners();
10628 // this.trigger.remove();
10631 // this.wrap.remove();
10633 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10637 onFocus : function(){
10638 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10640 if(!this.mimicing){
10641 this.wrap.addClass('x-trigger-wrap-focus');
10642 this.mimicing = true;
10643 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10644 if(this.monitorTab){
10645 this.el.on("keydown", this.checkTab, this);
10652 checkTab : function(e){
10653 if(e.getKey() == e.TAB){
10654 this.triggerBlur();
10659 onBlur : function(){
10664 mimicBlur : function(e, t){
10666 if(!this.wrap.contains(t) && this.validateBlur()){
10667 this.triggerBlur();
10673 triggerBlur : function(){
10674 this.mimicing = false;
10675 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10676 if(this.monitorTab){
10677 this.el.un("keydown", this.checkTab, this);
10679 //this.wrap.removeClass('x-trigger-wrap-focus');
10680 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10684 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10685 validateBlur : function(e, t){
10690 onDisable : function(){
10691 this.inputEl().dom.disabled = true;
10692 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10694 // this.wrap.addClass('x-item-disabled');
10699 onEnable : function(){
10700 this.inputEl().dom.disabled = false;
10701 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10703 // this.el.removeClass('x-item-disabled');
10708 onShow : function(){
10709 var ae = this.getActionEl();
10712 ae.dom.style.display = '';
10713 ae.dom.style.visibility = 'visible';
10719 onHide : function(){
10720 var ae = this.getActionEl();
10721 ae.dom.style.display = 'none';
10725 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10726 * by an implementing function.
10728 * @param {EventObject} e
10730 onTriggerClick : Roo.emptyFn
10734 * Ext JS Library 1.1.1
10735 * Copyright(c) 2006-2007, Ext JS, LLC.
10737 * Originally Released Under LGPL - original licence link has changed is not relivant.
10740 * <script type="text/javascript">
10745 * @class Roo.data.SortTypes
10747 * Defines the default sorting (casting?) comparison functions used when sorting data.
10749 Roo.data.SortTypes = {
10751 * Default sort that does nothing
10752 * @param {Mixed} s The value being converted
10753 * @return {Mixed} The comparison value
10755 none : function(s){
10760 * The regular expression used to strip tags
10764 stripTagsRE : /<\/?[^>]+>/gi,
10767 * Strips all HTML tags to sort on text only
10768 * @param {Mixed} s The value being converted
10769 * @return {String} The comparison value
10771 asText : function(s){
10772 return String(s).replace(this.stripTagsRE, "");
10776 * Strips all HTML tags to sort on text only - Case insensitive
10777 * @param {Mixed} s The value being converted
10778 * @return {String} The comparison value
10780 asUCText : function(s){
10781 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10785 * Case insensitive string
10786 * @param {Mixed} s The value being converted
10787 * @return {String} The comparison value
10789 asUCString : function(s) {
10790 return String(s).toUpperCase();
10795 * @param {Mixed} s The value being converted
10796 * @return {Number} The comparison value
10798 asDate : function(s) {
10802 if(s instanceof Date){
10803 return s.getTime();
10805 return Date.parse(String(s));
10810 * @param {Mixed} s The value being converted
10811 * @return {Float} The comparison value
10813 asFloat : function(s) {
10814 var val = parseFloat(String(s).replace(/,/g, ""));
10823 * @param {Mixed} s The value being converted
10824 * @return {Number} The comparison value
10826 asInt : function(s) {
10827 var val = parseInt(String(s).replace(/,/g, ""));
10835 * Ext JS Library 1.1.1
10836 * Copyright(c) 2006-2007, Ext JS, LLC.
10838 * Originally Released Under LGPL - original licence link has changed is not relivant.
10841 * <script type="text/javascript">
10845 * @class Roo.data.Record
10846 * Instances of this class encapsulate both record <em>definition</em> information, and record
10847 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10848 * to access Records cached in an {@link Roo.data.Store} object.<br>
10850 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10851 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10854 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10856 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10857 * {@link #create}. The parameters are the same.
10858 * @param {Array} data An associative Array of data values keyed by the field name.
10859 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10860 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10861 * not specified an integer id is generated.
10863 Roo.data.Record = function(data, id){
10864 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10869 * Generate a constructor for a specific record layout.
10870 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10871 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10872 * Each field definition object may contain the following properties: <ul>
10873 * <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,
10874 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10875 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10876 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10877 * is being used, then this is a string containing the javascript expression to reference the data relative to
10878 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10879 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10880 * this may be omitted.</p></li>
10881 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10882 * <ul><li>auto (Default, implies no conversion)</li>
10887 * <li>date</li></ul></p></li>
10888 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10889 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10890 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10891 * by the Reader into an object that will be stored in the Record. It is passed the
10892 * following parameters:<ul>
10893 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10895 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10897 * <br>usage:<br><pre><code>
10898 var TopicRecord = Roo.data.Record.create(
10899 {name: 'title', mapping: 'topic_title'},
10900 {name: 'author', mapping: 'username'},
10901 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10902 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10903 {name: 'lastPoster', mapping: 'user2'},
10904 {name: 'excerpt', mapping: 'post_text'}
10907 var myNewRecord = new TopicRecord({
10908 title: 'Do my job please',
10911 lastPost: new Date(),
10912 lastPoster: 'Animal',
10913 excerpt: 'No way dude!'
10915 myStore.add(myNewRecord);
10920 Roo.data.Record.create = function(o){
10921 var f = function(){
10922 f.superclass.constructor.apply(this, arguments);
10924 Roo.extend(f, Roo.data.Record);
10925 var p = f.prototype;
10926 p.fields = new Roo.util.MixedCollection(false, function(field){
10929 for(var i = 0, len = o.length; i < len; i++){
10930 p.fields.add(new Roo.data.Field(o[i]));
10932 f.getField = function(name){
10933 return p.fields.get(name);
10938 Roo.data.Record.AUTO_ID = 1000;
10939 Roo.data.Record.EDIT = 'edit';
10940 Roo.data.Record.REJECT = 'reject';
10941 Roo.data.Record.COMMIT = 'commit';
10943 Roo.data.Record.prototype = {
10945 * Readonly flag - true if this record has been modified.
10954 join : function(store){
10955 this.store = store;
10959 * Set the named field to the specified value.
10960 * @param {String} name The name of the field to set.
10961 * @param {Object} value The value to set the field to.
10963 set : function(name, value){
10964 if(this.data[name] == value){
10968 if(!this.modified){
10969 this.modified = {};
10971 if(typeof this.modified[name] == 'undefined'){
10972 this.modified[name] = this.data[name];
10974 this.data[name] = value;
10975 if(!this.editing && this.store){
10976 this.store.afterEdit(this);
10981 * Get the value of the named field.
10982 * @param {String} name The name of the field to get the value of.
10983 * @return {Object} The value of the field.
10985 get : function(name){
10986 return this.data[name];
10990 beginEdit : function(){
10991 this.editing = true;
10992 this.modified = {};
10996 cancelEdit : function(){
10997 this.editing = false;
10998 delete this.modified;
11002 endEdit : function(){
11003 this.editing = false;
11004 if(this.dirty && this.store){
11005 this.store.afterEdit(this);
11010 * Usually called by the {@link Roo.data.Store} which owns the Record.
11011 * Rejects all changes made to the Record since either creation, or the last commit operation.
11012 * Modified fields are reverted to their original values.
11014 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11015 * of reject operations.
11017 reject : function(){
11018 var m = this.modified;
11020 if(typeof m[n] != "function"){
11021 this.data[n] = m[n];
11024 this.dirty = false;
11025 delete this.modified;
11026 this.editing = false;
11028 this.store.afterReject(this);
11033 * Usually called by the {@link Roo.data.Store} which owns the Record.
11034 * Commits all changes made to the Record since either creation, or the last commit operation.
11036 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11037 * of commit operations.
11039 commit : function(){
11040 this.dirty = false;
11041 delete this.modified;
11042 this.editing = false;
11044 this.store.afterCommit(this);
11049 hasError : function(){
11050 return this.error != null;
11054 clearError : function(){
11059 * Creates a copy of this record.
11060 * @param {String} id (optional) A new record id if you don't want to use this record's id
11063 copy : function(newId) {
11064 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11068 * Ext JS Library 1.1.1
11069 * Copyright(c) 2006-2007, Ext JS, LLC.
11071 * Originally Released Under LGPL - original licence link has changed is not relivant.
11074 * <script type="text/javascript">
11080 * @class Roo.data.Store
11081 * @extends Roo.util.Observable
11082 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11083 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11085 * 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
11086 * has no knowledge of the format of the data returned by the Proxy.<br>
11088 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11089 * instances from the data object. These records are cached and made available through accessor functions.
11091 * Creates a new Store.
11092 * @param {Object} config A config object containing the objects needed for the Store to access data,
11093 * and read the data into Records.
11095 Roo.data.Store = function(config){
11096 this.data = new Roo.util.MixedCollection(false);
11097 this.data.getKey = function(o){
11100 this.baseParams = {};
11102 this.paramNames = {
11107 "multisort" : "_multisort"
11110 if(config && config.data){
11111 this.inlineData = config.data;
11112 delete config.data;
11115 Roo.apply(this, config);
11117 if(this.reader){ // reader passed
11118 this.reader = Roo.factory(this.reader, Roo.data);
11119 this.reader.xmodule = this.xmodule || false;
11120 if(!this.recordType){
11121 this.recordType = this.reader.recordType;
11123 if(this.reader.onMetaChange){
11124 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11128 if(this.recordType){
11129 this.fields = this.recordType.prototype.fields;
11131 this.modified = [];
11135 * @event datachanged
11136 * Fires when the data cache has changed, and a widget which is using this Store
11137 * as a Record cache should refresh its view.
11138 * @param {Store} this
11140 datachanged : true,
11142 * @event metachange
11143 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11144 * @param {Store} this
11145 * @param {Object} meta The JSON metadata
11150 * Fires when Records have been added to the Store
11151 * @param {Store} this
11152 * @param {Roo.data.Record[]} records The array of Records added
11153 * @param {Number} index The index at which the record(s) were added
11158 * Fires when a Record has been removed from the Store
11159 * @param {Store} this
11160 * @param {Roo.data.Record} record The Record that was removed
11161 * @param {Number} index The index at which the record was removed
11166 * Fires when a Record has been updated
11167 * @param {Store} this
11168 * @param {Roo.data.Record} record The Record that was updated
11169 * @param {String} operation The update operation being performed. Value may be one of:
11171 Roo.data.Record.EDIT
11172 Roo.data.Record.REJECT
11173 Roo.data.Record.COMMIT
11179 * Fires when the data cache has been cleared.
11180 * @param {Store} this
11184 * @event beforeload
11185 * Fires before a request is made for a new data object. If the beforeload handler returns false
11186 * the load action will be canceled.
11187 * @param {Store} this
11188 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11192 * @event beforeloadadd
11193 * Fires after a new set of Records has been loaded.
11194 * @param {Store} this
11195 * @param {Roo.data.Record[]} records The Records that were loaded
11196 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11198 beforeloadadd : true,
11201 * Fires after a new set of Records has been loaded, before they are added to the store.
11202 * @param {Store} this
11203 * @param {Roo.data.Record[]} records The Records that were loaded
11204 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11205 * @params {Object} return from reader
11209 * @event loadexception
11210 * Fires if an exception occurs in the Proxy during loading.
11211 * Called with the signature of the Proxy's "loadexception" event.
11212 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11215 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11216 * @param {Object} load options
11217 * @param {Object} jsonData from your request (normally this contains the Exception)
11219 loadexception : true
11223 this.proxy = Roo.factory(this.proxy, Roo.data);
11224 this.proxy.xmodule = this.xmodule || false;
11225 this.relayEvents(this.proxy, ["loadexception"]);
11227 this.sortToggle = {};
11228 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11230 Roo.data.Store.superclass.constructor.call(this);
11232 if(this.inlineData){
11233 this.loadData(this.inlineData);
11234 delete this.inlineData;
11238 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11240 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11241 * without a remote query - used by combo/forms at present.
11245 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11248 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11251 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11252 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11255 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11256 * on any HTTP request
11259 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11262 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11266 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11267 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11269 remoteSort : false,
11272 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11273 * loaded or when a record is removed. (defaults to false).
11275 pruneModifiedRecords : false,
11278 lastOptions : null,
11281 * Add Records to the Store and fires the add event.
11282 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11284 add : function(records){
11285 records = [].concat(records);
11286 for(var i = 0, len = records.length; i < len; i++){
11287 records[i].join(this);
11289 var index = this.data.length;
11290 this.data.addAll(records);
11291 this.fireEvent("add", this, records, index);
11295 * Remove a Record from the Store and fires the remove event.
11296 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11298 remove : function(record){
11299 var index = this.data.indexOf(record);
11300 this.data.removeAt(index);
11302 if(this.pruneModifiedRecords){
11303 this.modified.remove(record);
11305 this.fireEvent("remove", this, record, index);
11309 * Remove all Records from the Store and fires the clear event.
11311 removeAll : function(){
11313 if(this.pruneModifiedRecords){
11314 this.modified = [];
11316 this.fireEvent("clear", this);
11320 * Inserts Records to the Store at the given index and fires the add event.
11321 * @param {Number} index The start index at which to insert the passed Records.
11322 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11324 insert : function(index, records){
11325 records = [].concat(records);
11326 for(var i = 0, len = records.length; i < len; i++){
11327 this.data.insert(index, records[i]);
11328 records[i].join(this);
11330 this.fireEvent("add", this, records, index);
11334 * Get the index within the cache of the passed Record.
11335 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11336 * @return {Number} The index of the passed Record. Returns -1 if not found.
11338 indexOf : function(record){
11339 return this.data.indexOf(record);
11343 * Get the index within the cache of the Record with the passed id.
11344 * @param {String} id The id of the Record to find.
11345 * @return {Number} The index of the Record. Returns -1 if not found.
11347 indexOfId : function(id){
11348 return this.data.indexOfKey(id);
11352 * Get the Record with the specified id.
11353 * @param {String} id The id of the Record to find.
11354 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11356 getById : function(id){
11357 return this.data.key(id);
11361 * Get the Record at the specified index.
11362 * @param {Number} index The index of the Record to find.
11363 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11365 getAt : function(index){
11366 return this.data.itemAt(index);
11370 * Returns a range of Records between specified indices.
11371 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11372 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11373 * @return {Roo.data.Record[]} An array of Records
11375 getRange : function(start, end){
11376 return this.data.getRange(start, end);
11380 storeOptions : function(o){
11381 o = Roo.apply({}, o);
11384 this.lastOptions = o;
11388 * Loads the Record cache from the configured Proxy using the configured Reader.
11390 * If using remote paging, then the first load call must specify the <em>start</em>
11391 * and <em>limit</em> properties in the options.params property to establish the initial
11392 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11394 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11395 * and this call will return before the new data has been loaded. Perform any post-processing
11396 * in a callback function, or in a "load" event handler.</strong>
11398 * @param {Object} options An object containing properties which control loading options:<ul>
11399 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11400 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11401 * passed the following arguments:<ul>
11402 * <li>r : Roo.data.Record[]</li>
11403 * <li>options: Options object from the load call</li>
11404 * <li>success: Boolean success indicator</li></ul></li>
11405 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11406 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11409 load : function(options){
11410 options = options || {};
11411 if(this.fireEvent("beforeload", this, options) !== false){
11412 this.storeOptions(options);
11413 var p = Roo.apply(options.params || {}, this.baseParams);
11414 // if meta was not loaded from remote source.. try requesting it.
11415 if (!this.reader.metaFromRemote) {
11416 p._requestMeta = 1;
11418 if(this.sortInfo && this.remoteSort){
11419 var pn = this.paramNames;
11420 p[pn["sort"]] = this.sortInfo.field;
11421 p[pn["dir"]] = this.sortInfo.direction;
11423 if (this.multiSort) {
11424 var pn = this.paramNames;
11425 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11428 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11433 * Reloads the Record cache from the configured Proxy using the configured Reader and
11434 * the options from the last load operation performed.
11435 * @param {Object} options (optional) An object containing properties which may override the options
11436 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11437 * the most recently used options are reused).
11439 reload : function(options){
11440 this.load(Roo.applyIf(options||{}, this.lastOptions));
11444 // Called as a callback by the Reader during a load operation.
11445 loadRecords : function(o, options, success){
11446 if(!o || success === false){
11447 if(success !== false){
11448 this.fireEvent("load", this, [], options, o);
11450 if(options.callback){
11451 options.callback.call(options.scope || this, [], options, false);
11455 // if data returned failure - throw an exception.
11456 if (o.success === false) {
11457 // show a message if no listener is registered.
11458 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11459 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11461 // loadmask wil be hooked into this..
11462 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11465 var r = o.records, t = o.totalRecords || r.length;
11467 this.fireEvent("beforeloadadd", this, r, options, o);
11469 if(!options || options.add !== true){
11470 if(this.pruneModifiedRecords){
11471 this.modified = [];
11473 for(var i = 0, len = r.length; i < len; i++){
11477 this.data = this.snapshot;
11478 delete this.snapshot;
11481 this.data.addAll(r);
11482 this.totalLength = t;
11484 this.fireEvent("datachanged", this);
11486 this.totalLength = Math.max(t, this.data.length+r.length);
11490 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11492 var e = new Roo.data.Record({});
11494 e.set(this.parent.displayField, this.parent.emptyTitle);
11495 e.set(this.parent.valueField, '');
11500 this.fireEvent("load", this, r, options, o);
11501 if(options.callback){
11502 options.callback.call(options.scope || this, r, options, true);
11508 * Loads data from a passed data block. A Reader which understands the format of the data
11509 * must have been configured in the constructor.
11510 * @param {Object} data The data block from which to read the Records. The format of the data expected
11511 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11512 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11514 loadData : function(o, append){
11515 var r = this.reader.readRecords(o);
11516 this.loadRecords(r, {add: append}, true);
11520 * Gets the number of cached records.
11522 * <em>If using paging, this may not be the total size of the dataset. If the data object
11523 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11524 * the data set size</em>
11526 getCount : function(){
11527 return this.data.length || 0;
11531 * Gets the total number of records in the dataset as returned by the server.
11533 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11534 * the dataset size</em>
11536 getTotalCount : function(){
11537 return this.totalLength || 0;
11541 * Returns the sort state of the Store as an object with two properties:
11543 field {String} The name of the field by which the Records are sorted
11544 direction {String} The sort order, "ASC" or "DESC"
11547 getSortState : function(){
11548 return this.sortInfo;
11552 applySort : function(){
11553 if(this.sortInfo && !this.remoteSort){
11554 var s = this.sortInfo, f = s.field;
11555 var st = this.fields.get(f).sortType;
11556 var fn = function(r1, r2){
11557 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11558 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11560 this.data.sort(s.direction, fn);
11561 if(this.snapshot && this.snapshot != this.data){
11562 this.snapshot.sort(s.direction, fn);
11568 * Sets the default sort column and order to be used by the next load operation.
11569 * @param {String} fieldName The name of the field to sort by.
11570 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11572 setDefaultSort : function(field, dir){
11573 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11577 * Sort the Records.
11578 * If remote sorting is used, the sort is performed on the server, and the cache is
11579 * reloaded. If local sorting is used, the cache is sorted internally.
11580 * @param {String} fieldName The name of the field to sort by.
11581 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11583 sort : function(fieldName, dir){
11584 var f = this.fields.get(fieldName);
11586 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11588 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11589 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11594 this.sortToggle[f.name] = dir;
11595 this.sortInfo = {field: f.name, direction: dir};
11596 if(!this.remoteSort){
11598 this.fireEvent("datachanged", this);
11600 this.load(this.lastOptions);
11605 * Calls the specified function for each of the Records in the cache.
11606 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11607 * Returning <em>false</em> aborts and exits the iteration.
11608 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11610 each : function(fn, scope){
11611 this.data.each(fn, scope);
11615 * Gets all records modified since the last commit. Modified records are persisted across load operations
11616 * (e.g., during paging).
11617 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11619 getModifiedRecords : function(){
11620 return this.modified;
11624 createFilterFn : function(property, value, anyMatch){
11625 if(!value.exec){ // not a regex
11626 value = String(value);
11627 if(value.length == 0){
11630 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11632 return function(r){
11633 return value.test(r.data[property]);
11638 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11639 * @param {String} property A field on your records
11640 * @param {Number} start The record index to start at (defaults to 0)
11641 * @param {Number} end The last record index to include (defaults to length - 1)
11642 * @return {Number} The sum
11644 sum : function(property, start, end){
11645 var rs = this.data.items, v = 0;
11646 start = start || 0;
11647 end = (end || end === 0) ? end : rs.length-1;
11649 for(var i = start; i <= end; i++){
11650 v += (rs[i].data[property] || 0);
11656 * Filter the records by a specified property.
11657 * @param {String} field A field on your records
11658 * @param {String/RegExp} value Either a string that the field
11659 * should start with or a RegExp to test against the field
11660 * @param {Boolean} anyMatch True to match any part not just the beginning
11662 filter : function(property, value, anyMatch){
11663 var fn = this.createFilterFn(property, value, anyMatch);
11664 return fn ? this.filterBy(fn) : this.clearFilter();
11668 * Filter by a function. The specified function will be called with each
11669 * record in this data source. If the function returns true the record is included,
11670 * otherwise it is filtered.
11671 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11672 * @param {Object} scope (optional) The scope of the function (defaults to this)
11674 filterBy : function(fn, scope){
11675 this.snapshot = this.snapshot || this.data;
11676 this.data = this.queryBy(fn, scope||this);
11677 this.fireEvent("datachanged", this);
11681 * Query the records by a specified property.
11682 * @param {String} field A field on your records
11683 * @param {String/RegExp} value Either a string that the field
11684 * should start with or a RegExp to test against the field
11685 * @param {Boolean} anyMatch True to match any part not just the beginning
11686 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11688 query : function(property, value, anyMatch){
11689 var fn = this.createFilterFn(property, value, anyMatch);
11690 return fn ? this.queryBy(fn) : this.data.clone();
11694 * Query by a function. The specified function will be called with each
11695 * record in this data source. If the function returns true the record is included
11697 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11698 * @param {Object} scope (optional) The scope of the function (defaults to this)
11699 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11701 queryBy : function(fn, scope){
11702 var data = this.snapshot || this.data;
11703 return data.filterBy(fn, scope||this);
11707 * Collects unique values for a particular dataIndex from this store.
11708 * @param {String} dataIndex The property to collect
11709 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11710 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11711 * @return {Array} An array of the unique values
11713 collect : function(dataIndex, allowNull, bypassFilter){
11714 var d = (bypassFilter === true && this.snapshot) ?
11715 this.snapshot.items : this.data.items;
11716 var v, sv, r = [], l = {};
11717 for(var i = 0, len = d.length; i < len; i++){
11718 v = d[i].data[dataIndex];
11720 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11729 * Revert to a view of the Record cache with no filtering applied.
11730 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11732 clearFilter : function(suppressEvent){
11733 if(this.snapshot && this.snapshot != this.data){
11734 this.data = this.snapshot;
11735 delete this.snapshot;
11736 if(suppressEvent !== true){
11737 this.fireEvent("datachanged", this);
11743 afterEdit : function(record){
11744 if(this.modified.indexOf(record) == -1){
11745 this.modified.push(record);
11747 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11751 afterReject : function(record){
11752 this.modified.remove(record);
11753 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11757 afterCommit : function(record){
11758 this.modified.remove(record);
11759 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11763 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11764 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11766 commitChanges : function(){
11767 var m = this.modified.slice(0);
11768 this.modified = [];
11769 for(var i = 0, len = m.length; i < len; i++){
11775 * Cancel outstanding changes on all changed records.
11777 rejectChanges : function(){
11778 var m = this.modified.slice(0);
11779 this.modified = [];
11780 for(var i = 0, len = m.length; i < len; i++){
11785 onMetaChange : function(meta, rtype, o){
11786 this.recordType = rtype;
11787 this.fields = rtype.prototype.fields;
11788 delete this.snapshot;
11789 this.sortInfo = meta.sortInfo || this.sortInfo;
11790 this.modified = [];
11791 this.fireEvent('metachange', this, this.reader.meta);
11794 moveIndex : function(data, type)
11796 var index = this.indexOf(data);
11798 var newIndex = index + type;
11802 this.insert(newIndex, data);
11807 * Ext JS Library 1.1.1
11808 * Copyright(c) 2006-2007, Ext JS, LLC.
11810 * Originally Released Under LGPL - original licence link has changed is not relivant.
11813 * <script type="text/javascript">
11817 * @class Roo.data.SimpleStore
11818 * @extends Roo.data.Store
11819 * Small helper class to make creating Stores from Array data easier.
11820 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11821 * @cfg {Array} fields An array of field definition objects, or field name strings.
11822 * @cfg {Array} data The multi-dimensional array of data
11824 * @param {Object} config
11826 Roo.data.SimpleStore = function(config){
11827 Roo.data.SimpleStore.superclass.constructor.call(this, {
11829 reader: new Roo.data.ArrayReader({
11832 Roo.data.Record.create(config.fields)
11834 proxy : new Roo.data.MemoryProxy(config.data)
11838 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11840 * Ext JS Library 1.1.1
11841 * Copyright(c) 2006-2007, Ext JS, LLC.
11843 * Originally Released Under LGPL - original licence link has changed is not relivant.
11846 * <script type="text/javascript">
11851 * @extends Roo.data.Store
11852 * @class Roo.data.JsonStore
11853 * Small helper class to make creating Stores for JSON data easier. <br/>
11855 var store = new Roo.data.JsonStore({
11856 url: 'get-images.php',
11858 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11861 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11862 * JsonReader and HttpProxy (unless inline data is provided).</b>
11863 * @cfg {Array} fields An array of field definition objects, or field name strings.
11865 * @param {Object} config
11867 Roo.data.JsonStore = function(c){
11868 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11869 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11870 reader: new Roo.data.JsonReader(c, c.fields)
11873 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11875 * Ext JS Library 1.1.1
11876 * Copyright(c) 2006-2007, Ext JS, LLC.
11878 * Originally Released Under LGPL - original licence link has changed is not relivant.
11881 * <script type="text/javascript">
11885 Roo.data.Field = function(config){
11886 if(typeof config == "string"){
11887 config = {name: config};
11889 Roo.apply(this, config);
11892 this.type = "auto";
11895 var st = Roo.data.SortTypes;
11896 // named sortTypes are supported, here we look them up
11897 if(typeof this.sortType == "string"){
11898 this.sortType = st[this.sortType];
11901 // set default sortType for strings and dates
11902 if(!this.sortType){
11905 this.sortType = st.asUCString;
11908 this.sortType = st.asDate;
11911 this.sortType = st.none;
11916 var stripRe = /[\$,%]/g;
11918 // prebuilt conversion function for this field, instead of
11919 // switching every time we're reading a value
11921 var cv, dateFormat = this.dateFormat;
11926 cv = function(v){ return v; };
11929 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11933 return v !== undefined && v !== null && v !== '' ?
11934 parseInt(String(v).replace(stripRe, ""), 10) : '';
11939 return v !== undefined && v !== null && v !== '' ?
11940 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11945 cv = function(v){ return v === true || v === "true" || v == 1; };
11952 if(v instanceof Date){
11956 if(dateFormat == "timestamp"){
11957 return new Date(v*1000);
11959 return Date.parseDate(v, dateFormat);
11961 var parsed = Date.parse(v);
11962 return parsed ? new Date(parsed) : null;
11971 Roo.data.Field.prototype = {
11979 * Ext JS Library 1.1.1
11980 * Copyright(c) 2006-2007, Ext JS, LLC.
11982 * Originally Released Under LGPL - original licence link has changed is not relivant.
11985 * <script type="text/javascript">
11988 // Base class for reading structured data from a data source. This class is intended to be
11989 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11992 * @class Roo.data.DataReader
11993 * Base class for reading structured data from a data source. This class is intended to be
11994 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11997 Roo.data.DataReader = function(meta, recordType){
12001 this.recordType = recordType instanceof Array ?
12002 Roo.data.Record.create(recordType) : recordType;
12005 Roo.data.DataReader.prototype = {
12007 * Create an empty record
12008 * @param {Object} data (optional) - overlay some values
12009 * @return {Roo.data.Record} record created.
12011 newRow : function(d) {
12013 this.recordType.prototype.fields.each(function(c) {
12015 case 'int' : da[c.name] = 0; break;
12016 case 'date' : da[c.name] = new Date(); break;
12017 case 'float' : da[c.name] = 0.0; break;
12018 case 'boolean' : da[c.name] = false; break;
12019 default : da[c.name] = ""; break;
12023 return new this.recordType(Roo.apply(da, d));
12028 * Ext JS Library 1.1.1
12029 * Copyright(c) 2006-2007, Ext JS, LLC.
12031 * Originally Released Under LGPL - original licence link has changed is not relivant.
12034 * <script type="text/javascript">
12038 * @class Roo.data.DataProxy
12039 * @extends Roo.data.Observable
12040 * This class is an abstract base class for implementations which provide retrieval of
12041 * unformatted data objects.<br>
12043 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12044 * (of the appropriate type which knows how to parse the data object) to provide a block of
12045 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12047 * Custom implementations must implement the load method as described in
12048 * {@link Roo.data.HttpProxy#load}.
12050 Roo.data.DataProxy = function(){
12053 * @event beforeload
12054 * Fires before a network request is made to retrieve a data object.
12055 * @param {Object} This DataProxy object.
12056 * @param {Object} params The params parameter to the load function.
12061 * Fires before the load method's callback is called.
12062 * @param {Object} This DataProxy object.
12063 * @param {Object} o The data object.
12064 * @param {Object} arg The callback argument object passed to the load function.
12068 * @event loadexception
12069 * Fires if an Exception occurs during data retrieval.
12070 * @param {Object} This DataProxy object.
12071 * @param {Object} o The data object.
12072 * @param {Object} arg The callback argument object passed to the load function.
12073 * @param {Object} e The Exception.
12075 loadexception : true
12077 Roo.data.DataProxy.superclass.constructor.call(this);
12080 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12083 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12087 * Ext JS Library 1.1.1
12088 * Copyright(c) 2006-2007, Ext JS, LLC.
12090 * Originally Released Under LGPL - original licence link has changed is not relivant.
12093 * <script type="text/javascript">
12096 * @class Roo.data.MemoryProxy
12097 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12098 * to the Reader when its load method is called.
12100 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12102 Roo.data.MemoryProxy = function(data){
12106 Roo.data.MemoryProxy.superclass.constructor.call(this);
12110 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12113 * Load data from the requested source (in this case an in-memory
12114 * data object passed to the constructor), read the data object into
12115 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12116 * process that block using the passed callback.
12117 * @param {Object} params This parameter is not used by the MemoryProxy class.
12118 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12119 * object into a block of Roo.data.Records.
12120 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12121 * The function must be passed <ul>
12122 * <li>The Record block object</li>
12123 * <li>The "arg" argument from the load function</li>
12124 * <li>A boolean success indicator</li>
12126 * @param {Object} scope The scope in which to call the callback
12127 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12129 load : function(params, reader, callback, scope, arg){
12130 params = params || {};
12133 result = reader.readRecords(this.data);
12135 this.fireEvent("loadexception", this, arg, null, e);
12136 callback.call(scope, null, arg, false);
12139 callback.call(scope, result, arg, true);
12143 update : function(params, records){
12148 * Ext JS Library 1.1.1
12149 * Copyright(c) 2006-2007, Ext JS, LLC.
12151 * Originally Released Under LGPL - original licence link has changed is not relivant.
12154 * <script type="text/javascript">
12157 * @class Roo.data.HttpProxy
12158 * @extends Roo.data.DataProxy
12159 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12160 * configured to reference a certain URL.<br><br>
12162 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12163 * from which the running page was served.<br><br>
12165 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12167 * Be aware that to enable the browser to parse an XML document, the server must set
12168 * the Content-Type header in the HTTP response to "text/xml".
12170 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12171 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12172 * will be used to make the request.
12174 Roo.data.HttpProxy = function(conn){
12175 Roo.data.HttpProxy.superclass.constructor.call(this);
12176 // is conn a conn config or a real conn?
12178 this.useAjax = !conn || !conn.events;
12182 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12183 // thse are take from connection...
12186 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12189 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12190 * extra parameters to each request made by this object. (defaults to undefined)
12193 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12194 * to each request made by this object. (defaults to undefined)
12197 * @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)
12200 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12203 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12209 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12213 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12214 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12215 * a finer-grained basis than the DataProxy events.
12217 getConnection : function(){
12218 return this.useAjax ? Roo.Ajax : this.conn;
12222 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12223 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12224 * process that block using the passed callback.
12225 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12226 * for the request to the remote server.
12227 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12228 * object into a block of Roo.data.Records.
12229 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12230 * The function must be passed <ul>
12231 * <li>The Record block object</li>
12232 * <li>The "arg" argument from the load function</li>
12233 * <li>A boolean success indicator</li>
12235 * @param {Object} scope The scope in which to call the callback
12236 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12238 load : function(params, reader, callback, scope, arg){
12239 if(this.fireEvent("beforeload", this, params) !== false){
12241 params : params || {},
12243 callback : callback,
12248 callback : this.loadResponse,
12252 Roo.applyIf(o, this.conn);
12253 if(this.activeRequest){
12254 Roo.Ajax.abort(this.activeRequest);
12256 this.activeRequest = Roo.Ajax.request(o);
12258 this.conn.request(o);
12261 callback.call(scope||this, null, arg, false);
12266 loadResponse : function(o, success, response){
12267 delete this.activeRequest;
12269 this.fireEvent("loadexception", this, o, response);
12270 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12275 result = o.reader.read(response);
12277 this.fireEvent("loadexception", this, o, response, e);
12278 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12282 this.fireEvent("load", this, o, o.request.arg);
12283 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12287 update : function(dataSet){
12292 updateResponse : function(dataSet){
12297 * Ext JS Library 1.1.1
12298 * Copyright(c) 2006-2007, Ext JS, LLC.
12300 * Originally Released Under LGPL - original licence link has changed is not relivant.
12303 * <script type="text/javascript">
12307 * @class Roo.data.ScriptTagProxy
12308 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12309 * other than the originating domain of the running page.<br><br>
12311 * <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
12312 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12314 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12315 * source code that is used as the source inside a <script> tag.<br><br>
12317 * In order for the browser to process the returned data, the server must wrap the data object
12318 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12319 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12320 * depending on whether the callback name was passed:
12323 boolean scriptTag = false;
12324 String cb = request.getParameter("callback");
12327 response.setContentType("text/javascript");
12329 response.setContentType("application/x-json");
12331 Writer out = response.getWriter();
12333 out.write(cb + "(");
12335 out.print(dataBlock.toJsonString());
12342 * @param {Object} config A configuration object.
12344 Roo.data.ScriptTagProxy = function(config){
12345 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12346 Roo.apply(this, config);
12347 this.head = document.getElementsByTagName("head")[0];
12350 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12352 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12354 * @cfg {String} url The URL from which to request the data object.
12357 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12361 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12362 * the server the name of the callback function set up by the load call to process the returned data object.
12363 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12364 * javascript output which calls this named function passing the data object as its only parameter.
12366 callbackParam : "callback",
12368 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12369 * name to the request.
12374 * Load data from the configured URL, read the data object into
12375 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12376 * process that block using the passed callback.
12377 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12378 * for the request to the remote server.
12379 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12380 * object into a block of Roo.data.Records.
12381 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12382 * The function must be passed <ul>
12383 * <li>The Record block object</li>
12384 * <li>The "arg" argument from the load function</li>
12385 * <li>A boolean success indicator</li>
12387 * @param {Object} scope The scope in which to call the callback
12388 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12390 load : function(params, reader, callback, scope, arg){
12391 if(this.fireEvent("beforeload", this, params) !== false){
12393 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12395 var url = this.url;
12396 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12398 url += "&_dc=" + (new Date().getTime());
12400 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12403 cb : "stcCallback"+transId,
12404 scriptId : "stcScript"+transId,
12408 callback : callback,
12414 window[trans.cb] = function(o){
12415 conn.handleResponse(o, trans);
12418 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12420 if(this.autoAbort !== false){
12424 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12426 var script = document.createElement("script");
12427 script.setAttribute("src", url);
12428 script.setAttribute("type", "text/javascript");
12429 script.setAttribute("id", trans.scriptId);
12430 this.head.appendChild(script);
12432 this.trans = trans;
12434 callback.call(scope||this, null, arg, false);
12439 isLoading : function(){
12440 return this.trans ? true : false;
12444 * Abort the current server request.
12446 abort : function(){
12447 if(this.isLoading()){
12448 this.destroyTrans(this.trans);
12453 destroyTrans : function(trans, isLoaded){
12454 this.head.removeChild(document.getElementById(trans.scriptId));
12455 clearTimeout(trans.timeoutId);
12457 window[trans.cb] = undefined;
12459 delete window[trans.cb];
12462 // if hasn't been loaded, wait for load to remove it to prevent script error
12463 window[trans.cb] = function(){
12464 window[trans.cb] = undefined;
12466 delete window[trans.cb];
12473 handleResponse : function(o, trans){
12474 this.trans = false;
12475 this.destroyTrans(trans, true);
12478 result = trans.reader.readRecords(o);
12480 this.fireEvent("loadexception", this, o, trans.arg, e);
12481 trans.callback.call(trans.scope||window, null, trans.arg, false);
12484 this.fireEvent("load", this, o, trans.arg);
12485 trans.callback.call(trans.scope||window, result, trans.arg, true);
12489 handleFailure : function(trans){
12490 this.trans = false;
12491 this.destroyTrans(trans, false);
12492 this.fireEvent("loadexception", this, null, trans.arg);
12493 trans.callback.call(trans.scope||window, null, trans.arg, false);
12497 * Ext JS Library 1.1.1
12498 * Copyright(c) 2006-2007, Ext JS, LLC.
12500 * Originally Released Under LGPL - original licence link has changed is not relivant.
12503 * <script type="text/javascript">
12507 * @class Roo.data.JsonReader
12508 * @extends Roo.data.DataReader
12509 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12510 * based on mappings in a provided Roo.data.Record constructor.
12512 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12513 * in the reply previously.
12518 var RecordDef = Roo.data.Record.create([
12519 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12520 {name: 'occupation'} // This field will use "occupation" as the mapping.
12522 var myReader = new Roo.data.JsonReader({
12523 totalProperty: "results", // The property which contains the total dataset size (optional)
12524 root: "rows", // The property which contains an Array of row objects
12525 id: "id" // The property within each row object that provides an ID for the record (optional)
12529 * This would consume a JSON file like this:
12531 { 'results': 2, 'rows': [
12532 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12533 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12536 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12537 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12538 * paged from the remote server.
12539 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12540 * @cfg {String} root name of the property which contains the Array of row objects.
12541 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12542 * @cfg {Array} fields Array of field definition objects
12544 * Create a new JsonReader
12545 * @param {Object} meta Metadata configuration options
12546 * @param {Object} recordType Either an Array of field definition objects,
12547 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12549 Roo.data.JsonReader = function(meta, recordType){
12552 // set some defaults:
12553 Roo.applyIf(meta, {
12554 totalProperty: 'total',
12555 successProperty : 'success',
12560 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12562 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12565 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12566 * Used by Store query builder to append _requestMeta to params.
12569 metaFromRemote : false,
12571 * This method is only used by a DataProxy which has retrieved data from a remote server.
12572 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12573 * @return {Object} data A data block which is used by an Roo.data.Store object as
12574 * a cache of Roo.data.Records.
12576 read : function(response){
12577 var json = response.responseText;
12579 var o = /* eval:var:o */ eval("("+json+")");
12581 throw {message: "JsonReader.read: Json object not found"};
12587 this.metaFromRemote = true;
12588 this.meta = o.metaData;
12589 this.recordType = Roo.data.Record.create(o.metaData.fields);
12590 this.onMetaChange(this.meta, this.recordType, o);
12592 return this.readRecords(o);
12595 // private function a store will implement
12596 onMetaChange : function(meta, recordType, o){
12603 simpleAccess: function(obj, subsc) {
12610 getJsonAccessor: function(){
12612 return function(expr) {
12614 return(re.test(expr))
12615 ? new Function("obj", "return obj." + expr)
12620 return Roo.emptyFn;
12625 * Create a data block containing Roo.data.Records from an XML document.
12626 * @param {Object} o An object which contains an Array of row objects in the property specified
12627 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12628 * which contains the total size of the dataset.
12629 * @return {Object} data A data block which is used by an Roo.data.Store object as
12630 * a cache of Roo.data.Records.
12632 readRecords : function(o){
12634 * After any data loads, the raw JSON data is available for further custom processing.
12638 var s = this.meta, Record = this.recordType,
12639 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12641 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12643 if(s.totalProperty) {
12644 this.getTotal = this.getJsonAccessor(s.totalProperty);
12646 if(s.successProperty) {
12647 this.getSuccess = this.getJsonAccessor(s.successProperty);
12649 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12651 var g = this.getJsonAccessor(s.id);
12652 this.getId = function(rec) {
12654 return (r === undefined || r === "") ? null : r;
12657 this.getId = function(){return null;};
12660 for(var jj = 0; jj < fl; jj++){
12662 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12663 this.ef[jj] = this.getJsonAccessor(map);
12667 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12668 if(s.totalProperty){
12669 var vt = parseInt(this.getTotal(o), 10);
12674 if(s.successProperty){
12675 var vs = this.getSuccess(o);
12676 if(vs === false || vs === 'false'){
12681 for(var i = 0; i < c; i++){
12684 var id = this.getId(n);
12685 for(var j = 0; j < fl; j++){
12687 var v = this.ef[j](n);
12689 Roo.log('missing convert for ' + f.name);
12693 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12695 var record = new Record(values, id);
12697 records[i] = record;
12703 totalRecords : totalRecords
12708 * Ext JS Library 1.1.1
12709 * Copyright(c) 2006-2007, Ext JS, LLC.
12711 * Originally Released Under LGPL - original licence link has changed is not relivant.
12714 * <script type="text/javascript">
12718 * @class Roo.data.ArrayReader
12719 * @extends Roo.data.DataReader
12720 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12721 * Each element of that Array represents a row of data fields. The
12722 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12723 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12727 var RecordDef = Roo.data.Record.create([
12728 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12729 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12731 var myReader = new Roo.data.ArrayReader({
12732 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12736 * This would consume an Array like this:
12738 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12740 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12742 * Create a new JsonReader
12743 * @param {Object} meta Metadata configuration options.
12744 * @param {Object} recordType Either an Array of field definition objects
12745 * as specified to {@link Roo.data.Record#create},
12746 * or an {@link Roo.data.Record} object
12747 * created using {@link Roo.data.Record#create}.
12749 Roo.data.ArrayReader = function(meta, recordType){
12750 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12753 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12755 * Create a data block containing Roo.data.Records from an XML document.
12756 * @param {Object} o An Array of row objects which represents the dataset.
12757 * @return {Object} data A data block which is used by an Roo.data.Store object as
12758 * a cache of Roo.data.Records.
12760 readRecords : function(o){
12761 var sid = this.meta ? this.meta.id : null;
12762 var recordType = this.recordType, fields = recordType.prototype.fields;
12765 for(var i = 0; i < root.length; i++){
12768 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12769 for(var j = 0, jlen = fields.length; j < jlen; j++){
12770 var f = fields.items[j];
12771 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12772 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12774 values[f.name] = v;
12776 var record = new recordType(values, id);
12778 records[records.length] = record;
12782 totalRecords : records.length
12791 * @class Roo.bootstrap.ComboBox
12792 * @extends Roo.bootstrap.TriggerField
12793 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12794 * @cfg {Boolean} append (true|false) default false
12795 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12796 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12797 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12798 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12799 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12800 * @cfg {Boolean} animate default true
12801 * @cfg {Boolean} emptyResultText only for touch device
12802 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12803 * @cfg {String} emptyTitle default ''
12805 * Create a new ComboBox.
12806 * @param {Object} config Configuration options
12808 Roo.bootstrap.ComboBox = function(config){
12809 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12813 * Fires when the dropdown list is expanded
12814 * @param {Roo.bootstrap.ComboBox} combo This combo box
12819 * Fires when the dropdown list is collapsed
12820 * @param {Roo.bootstrap.ComboBox} combo This combo box
12824 * @event beforeselect
12825 * Fires before a list item is selected. Return false to cancel the selection.
12826 * @param {Roo.bootstrap.ComboBox} combo This combo box
12827 * @param {Roo.data.Record} record The data record returned from the underlying store
12828 * @param {Number} index The index of the selected item in the dropdown list
12830 'beforeselect' : true,
12833 * Fires when a list item is selected
12834 * @param {Roo.bootstrap.ComboBox} combo This combo box
12835 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12836 * @param {Number} index The index of the selected item in the dropdown list
12840 * @event beforequery
12841 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12842 * The event object passed has these properties:
12843 * @param {Roo.bootstrap.ComboBox} combo This combo box
12844 * @param {String} query The query
12845 * @param {Boolean} forceAll true to force "all" query
12846 * @param {Boolean} cancel true to cancel the query
12847 * @param {Object} e The query event object
12849 'beforequery': true,
12852 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12853 * @param {Roo.bootstrap.ComboBox} combo This combo box
12858 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12859 * @param {Roo.bootstrap.ComboBox} combo This combo box
12860 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12865 * Fires when the remove value from the combobox array
12866 * @param {Roo.bootstrap.ComboBox} combo This combo box
12870 * @event afterremove
12871 * Fires when the remove value from the combobox array
12872 * @param {Roo.bootstrap.ComboBox} combo This combo box
12874 'afterremove' : true,
12876 * @event specialfilter
12877 * Fires when specialfilter
12878 * @param {Roo.bootstrap.ComboBox} combo This combo box
12880 'specialfilter' : true,
12883 * Fires when tick the element
12884 * @param {Roo.bootstrap.ComboBox} combo This combo box
12888 * @event touchviewdisplay
12889 * Fires when touch view require special display (default is using displayField)
12890 * @param {Roo.bootstrap.ComboBox} combo This combo box
12891 * @param {Object} cfg set html .
12893 'touchviewdisplay' : true
12898 this.tickItems = [];
12900 this.selectedIndex = -1;
12901 if(this.mode == 'local'){
12902 if(config.queryDelay === undefined){
12903 this.queryDelay = 10;
12905 if(config.minChars === undefined){
12911 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12914 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12915 * rendering into an Roo.Editor, defaults to false)
12918 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12919 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12922 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12925 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12926 * the dropdown list (defaults to undefined, with no header element)
12930 * @cfg {String/Roo.Template} tpl The template to use to render the output
12934 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12936 listWidth: undefined,
12938 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12939 * mode = 'remote' or 'text' if mode = 'local')
12941 displayField: undefined,
12944 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12945 * mode = 'remote' or 'value' if mode = 'local').
12946 * Note: use of a valueField requires the user make a selection
12947 * in order for a value to be mapped.
12949 valueField: undefined,
12951 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12956 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12957 * field's data value (defaults to the underlying DOM element's name)
12959 hiddenName: undefined,
12961 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12965 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12967 selectedClass: 'active',
12970 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12974 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12975 * anchor positions (defaults to 'tl-bl')
12977 listAlign: 'tl-bl?',
12979 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12983 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12984 * query specified by the allQuery config option (defaults to 'query')
12986 triggerAction: 'query',
12988 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12989 * (defaults to 4, does not apply if editable = false)
12993 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12994 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12998 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12999 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13003 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13004 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13008 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13009 * when editable = true (defaults to false)
13011 selectOnFocus:false,
13013 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13015 queryParam: 'query',
13017 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13018 * when mode = 'remote' (defaults to 'Loading...')
13020 loadingText: 'Loading...',
13022 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13026 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13030 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13031 * traditional select (defaults to true)
13035 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13039 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13043 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13044 * listWidth has a higher value)
13048 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13049 * allow the user to set arbitrary text into the field (defaults to false)
13051 forceSelection:false,
13053 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13054 * if typeAhead = true (defaults to 250)
13056 typeAheadDelay : 250,
13058 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13059 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13061 valueNotFoundText : undefined,
13063 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13065 blockFocus : false,
13068 * @cfg {Boolean} disableClear Disable showing of clear button.
13070 disableClear : false,
13072 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13074 alwaysQuery : false,
13077 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13082 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13084 invalidClass : "has-warning",
13087 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13089 validClass : "has-success",
13092 * @cfg {Boolean} specialFilter (true|false) special filter default false
13094 specialFilter : false,
13097 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13099 mobileTouchView : true,
13102 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13104 useNativeIOS : false,
13107 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13109 mobile_restrict_height : false,
13111 ios_options : false,
13123 btnPosition : 'right',
13124 triggerList : true,
13125 showToggleBtn : true,
13127 emptyResultText: 'Empty',
13128 triggerText : 'Select',
13131 // element that contains real text value.. (when hidden is used..)
13133 getAutoCreate : function()
13138 * Render classic select for iso
13141 if(Roo.isIOS && this.useNativeIOS){
13142 cfg = this.getAutoCreateNativeIOS();
13150 if(Roo.isTouch && this.mobileTouchView){
13151 cfg = this.getAutoCreateTouchView();
13158 if(!this.tickable){
13159 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13164 * ComboBox with tickable selections
13167 var align = this.labelAlign || this.parentLabelAlign();
13170 cls : 'form-group roo-combobox-tickable' //input-group
13173 var btn_text_select = '';
13174 var btn_text_done = '';
13175 var btn_text_cancel = '';
13177 if (this.btn_text_show) {
13178 btn_text_select = 'Select';
13179 btn_text_done = 'Done';
13180 btn_text_cancel = 'Cancel';
13185 cls : 'tickable-buttons',
13190 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13191 //html : this.triggerText
13192 html: btn_text_select
13198 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13200 html: btn_text_done
13206 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13208 html: btn_text_cancel
13214 buttons.cn.unshift({
13216 cls: 'roo-select2-search-field-input'
13222 Roo.each(buttons.cn, function(c){
13224 c.cls += ' btn-' + _this.size;
13227 if (_this.disabled) {
13238 cls: 'form-hidden-field'
13242 cls: 'roo-select2-choices',
13246 cls: 'roo-select2-search-field',
13257 cls: 'roo-select2-container input-group roo-select2-container-multi',
13262 // cls: 'typeahead typeahead-long dropdown-menu',
13263 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13268 if(this.hasFeedback && !this.allowBlank){
13272 cls: 'glyphicon form-control-feedback'
13275 combobox.cn.push(feedback);
13279 if (align ==='left' && this.fieldLabel.length) {
13281 cfg.cls += ' roo-form-group-label-left';
13286 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13287 tooltip : 'This field is required'
13292 cls : 'control-label',
13293 html : this.fieldLabel
13305 var labelCfg = cfg.cn[1];
13306 var contentCfg = cfg.cn[2];
13309 if(this.indicatorpos == 'right'){
13315 cls : 'control-label',
13319 html : this.fieldLabel
13323 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13324 tooltip : 'This field is required'
13339 labelCfg = cfg.cn[0];
13340 contentCfg = cfg.cn[1];
13344 if(this.labelWidth > 12){
13345 labelCfg.style = "width: " + this.labelWidth + 'px';
13348 if(this.labelWidth < 13 && this.labelmd == 0){
13349 this.labelmd = this.labelWidth;
13352 if(this.labellg > 0){
13353 labelCfg.cls += ' col-lg-' + this.labellg;
13354 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13357 if(this.labelmd > 0){
13358 labelCfg.cls += ' col-md-' + this.labelmd;
13359 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13362 if(this.labelsm > 0){
13363 labelCfg.cls += ' col-sm-' + this.labelsm;
13364 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13367 if(this.labelxs > 0){
13368 labelCfg.cls += ' col-xs-' + this.labelxs;
13369 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13373 } else if ( this.fieldLabel.length) {
13374 // Roo.log(" label");
13378 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13379 tooltip : 'This field is required'
13383 //cls : 'input-group-addon',
13384 html : this.fieldLabel
13389 if(this.indicatorpos == 'right'){
13393 //cls : 'input-group-addon',
13394 html : this.fieldLabel
13398 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13399 tooltip : 'This field is required'
13408 // Roo.log(" no label && no align");
13415 ['xs','sm','md','lg'].map(function(size){
13416 if (settings[size]) {
13417 cfg.cls += ' col-' + size + '-' + settings[size];
13425 _initEventsCalled : false,
13428 initEvents: function()
13430 if (this._initEventsCalled) { // as we call render... prevent looping...
13433 this._initEventsCalled = true;
13436 throw "can not find store for combo";
13439 this.indicator = this.indicatorEl();
13441 this.store = Roo.factory(this.store, Roo.data);
13442 this.store.parent = this;
13444 // if we are building from html. then this element is so complex, that we can not really
13445 // use the rendered HTML.
13446 // so we have to trash and replace the previous code.
13447 if (Roo.XComponent.build_from_html) {
13448 // remove this element....
13449 var e = this.el.dom, k=0;
13450 while (e ) { e = e.previousSibling; ++k;}
13455 this.rendered = false;
13457 this.render(this.parent().getChildContainer(true), k);
13460 if(Roo.isIOS && this.useNativeIOS){
13461 this.initIOSView();
13469 if(Roo.isTouch && this.mobileTouchView){
13470 this.initTouchView();
13475 this.initTickableEvents();
13479 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13481 if(this.hiddenName){
13483 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13485 this.hiddenField.dom.value =
13486 this.hiddenValue !== undefined ? this.hiddenValue :
13487 this.value !== undefined ? this.value : '';
13489 // prevent input submission
13490 this.el.dom.removeAttribute('name');
13491 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13496 // this.el.dom.setAttribute('autocomplete', 'off');
13499 var cls = 'x-combo-list';
13501 //this.list = new Roo.Layer({
13502 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13508 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13509 _this.list.setWidth(lw);
13512 this.list.on('mouseover', this.onViewOver, this);
13513 this.list.on('mousemove', this.onViewMove, this);
13514 this.list.on('scroll', this.onViewScroll, this);
13517 this.list.swallowEvent('mousewheel');
13518 this.assetHeight = 0;
13521 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13522 this.assetHeight += this.header.getHeight();
13525 this.innerList = this.list.createChild({cls:cls+'-inner'});
13526 this.innerList.on('mouseover', this.onViewOver, this);
13527 this.innerList.on('mousemove', this.onViewMove, this);
13528 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13530 if(this.allowBlank && !this.pageSize && !this.disableClear){
13531 this.footer = this.list.createChild({cls:cls+'-ft'});
13532 this.pageTb = new Roo.Toolbar(this.footer);
13536 this.footer = this.list.createChild({cls:cls+'-ft'});
13537 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13538 {pageSize: this.pageSize});
13542 if (this.pageTb && this.allowBlank && !this.disableClear) {
13544 this.pageTb.add(new Roo.Toolbar.Fill(), {
13545 cls: 'x-btn-icon x-btn-clear',
13547 handler: function()
13550 _this.clearValue();
13551 _this.onSelect(false, -1);
13556 this.assetHeight += this.footer.getHeight();
13561 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13564 this.view = new Roo.View(this.list, this.tpl, {
13565 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13567 //this.view.wrapEl.setDisplayed(false);
13568 this.view.on('click', this.onViewClick, this);
13571 this.store.on('beforeload', this.onBeforeLoad, this);
13572 this.store.on('load', this.onLoad, this);
13573 this.store.on('loadexception', this.onLoadException, this);
13575 if(this.resizable){
13576 this.resizer = new Roo.Resizable(this.list, {
13577 pinned:true, handles:'se'
13579 this.resizer.on('resize', function(r, w, h){
13580 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13581 this.listWidth = w;
13582 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13583 this.restrictHeight();
13585 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13588 if(!this.editable){
13589 this.editable = true;
13590 this.setEditable(false);
13595 if (typeof(this.events.add.listeners) != 'undefined') {
13597 this.addicon = this.wrap.createChild(
13598 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13600 this.addicon.on('click', function(e) {
13601 this.fireEvent('add', this);
13604 if (typeof(this.events.edit.listeners) != 'undefined') {
13606 this.editicon = this.wrap.createChild(
13607 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13608 if (this.addicon) {
13609 this.editicon.setStyle('margin-left', '40px');
13611 this.editicon.on('click', function(e) {
13613 // we fire even if inothing is selected..
13614 this.fireEvent('edit', this, this.lastData );
13620 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13621 "up" : function(e){
13622 this.inKeyMode = true;
13626 "down" : function(e){
13627 if(!this.isExpanded()){
13628 this.onTriggerClick();
13630 this.inKeyMode = true;
13635 "enter" : function(e){
13636 // this.onViewClick();
13640 if(this.fireEvent("specialkey", this, e)){
13641 this.onViewClick(false);
13647 "esc" : function(e){
13651 "tab" : function(e){
13654 if(this.fireEvent("specialkey", this, e)){
13655 this.onViewClick(false);
13663 doRelay : function(foo, bar, hname){
13664 if(hname == 'down' || this.scope.isExpanded()){
13665 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13674 this.queryDelay = Math.max(this.queryDelay || 10,
13675 this.mode == 'local' ? 10 : 250);
13678 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13680 if(this.typeAhead){
13681 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13683 if(this.editable !== false){
13684 this.inputEl().on("keyup", this.onKeyUp, this);
13686 if(this.forceSelection){
13687 this.inputEl().on('blur', this.doForce, this);
13691 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13692 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13696 initTickableEvents: function()
13700 if(this.hiddenName){
13702 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13704 this.hiddenField.dom.value =
13705 this.hiddenValue !== undefined ? this.hiddenValue :
13706 this.value !== undefined ? this.value : '';
13708 // prevent input submission
13709 this.el.dom.removeAttribute('name');
13710 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13715 // this.list = this.el.select('ul.dropdown-menu',true).first();
13717 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13718 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13719 if(this.triggerList){
13720 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13723 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13724 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13726 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13727 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13729 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13730 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13732 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13733 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13734 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13737 this.cancelBtn.hide();
13742 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13743 _this.list.setWidth(lw);
13746 this.list.on('mouseover', this.onViewOver, this);
13747 this.list.on('mousemove', this.onViewMove, this);
13749 this.list.on('scroll', this.onViewScroll, this);
13752 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13753 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13756 this.view = new Roo.View(this.list, this.tpl, {
13761 selectedClass: this.selectedClass
13764 //this.view.wrapEl.setDisplayed(false);
13765 this.view.on('click', this.onViewClick, this);
13769 this.store.on('beforeload', this.onBeforeLoad, this);
13770 this.store.on('load', this.onLoad, this);
13771 this.store.on('loadexception', this.onLoadException, this);
13774 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13775 "up" : function(e){
13776 this.inKeyMode = true;
13780 "down" : function(e){
13781 this.inKeyMode = true;
13785 "enter" : function(e){
13786 if(this.fireEvent("specialkey", this, e)){
13787 this.onViewClick(false);
13793 "esc" : function(e){
13794 this.onTickableFooterButtonClick(e, false, false);
13797 "tab" : function(e){
13798 this.fireEvent("specialkey", this, e);
13800 this.onTickableFooterButtonClick(e, false, false);
13807 doRelay : function(e, fn, key){
13808 if(this.scope.isExpanded()){
13809 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13818 this.queryDelay = Math.max(this.queryDelay || 10,
13819 this.mode == 'local' ? 10 : 250);
13822 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13824 if(this.typeAhead){
13825 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13828 if(this.editable !== false){
13829 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13832 this.indicator = this.indicatorEl();
13834 if(this.indicator){
13835 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13836 this.indicator.hide();
13841 onDestroy : function(){
13843 this.view.setStore(null);
13844 this.view.el.removeAllListeners();
13845 this.view.el.remove();
13846 this.view.purgeListeners();
13849 this.list.dom.innerHTML = '';
13853 this.store.un('beforeload', this.onBeforeLoad, this);
13854 this.store.un('load', this.onLoad, this);
13855 this.store.un('loadexception', this.onLoadException, this);
13857 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13861 fireKey : function(e){
13862 if(e.isNavKeyPress() && !this.list.isVisible()){
13863 this.fireEvent("specialkey", this, e);
13868 onResize: function(w, h){
13869 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13871 // if(typeof w != 'number'){
13872 // // we do not handle it!?!?
13875 // var tw = this.trigger.getWidth();
13876 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13877 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13879 // this.inputEl().setWidth( this.adjustWidth('input', x));
13881 // //this.trigger.setStyle('left', x+'px');
13883 // if(this.list && this.listWidth === undefined){
13884 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13885 // this.list.setWidth(lw);
13886 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13894 * Allow or prevent the user from directly editing the field text. If false is passed,
13895 * the user will only be able to select from the items defined in the dropdown list. This method
13896 * is the runtime equivalent of setting the 'editable' config option at config time.
13897 * @param {Boolean} value True to allow the user to directly edit the field text
13899 setEditable : function(value){
13900 if(value == this.editable){
13903 this.editable = value;
13905 this.inputEl().dom.setAttribute('readOnly', true);
13906 this.inputEl().on('mousedown', this.onTriggerClick, this);
13907 this.inputEl().addClass('x-combo-noedit');
13909 this.inputEl().dom.setAttribute('readOnly', false);
13910 this.inputEl().un('mousedown', this.onTriggerClick, this);
13911 this.inputEl().removeClass('x-combo-noedit');
13917 onBeforeLoad : function(combo,opts){
13918 if(!this.hasFocus){
13922 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13924 this.restrictHeight();
13925 this.selectedIndex = -1;
13929 onLoad : function(){
13931 this.hasQuery = false;
13933 if(!this.hasFocus){
13937 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13938 this.loading.hide();
13941 if(this.store.getCount() > 0){
13944 this.restrictHeight();
13945 if(this.lastQuery == this.allQuery){
13946 if(this.editable && !this.tickable){
13947 this.inputEl().dom.select();
13951 !this.selectByValue(this.value, true) &&
13954 !this.store.lastOptions ||
13955 typeof(this.store.lastOptions.add) == 'undefined' ||
13956 this.store.lastOptions.add != true
13959 this.select(0, true);
13962 if(this.autoFocus){
13965 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13966 this.taTask.delay(this.typeAheadDelay);
13970 this.onEmptyResults();
13976 onLoadException : function()
13978 this.hasQuery = false;
13980 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13981 this.loading.hide();
13984 if(this.tickable && this.editable){
13989 // only causes errors at present
13990 //Roo.log(this.store.reader.jsonData);
13991 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13993 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13999 onTypeAhead : function(){
14000 if(this.store.getCount() > 0){
14001 var r = this.store.getAt(0);
14002 var newValue = r.data[this.displayField];
14003 var len = newValue.length;
14004 var selStart = this.getRawValue().length;
14006 if(selStart != len){
14007 this.setRawValue(newValue);
14008 this.selectText(selStart, newValue.length);
14014 onSelect : function(record, index){
14016 if(this.fireEvent('beforeselect', this, record, index) !== false){
14018 this.setFromData(index > -1 ? record.data : false);
14021 this.fireEvent('select', this, record, index);
14026 * Returns the currently selected field value or empty string if no value is set.
14027 * @return {String} value The selected value
14029 getValue : function()
14031 if(Roo.isIOS && this.useNativeIOS){
14032 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14036 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14039 if(this.valueField){
14040 return typeof this.value != 'undefined' ? this.value : '';
14042 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14046 getRawValue : function()
14048 if(Roo.isIOS && this.useNativeIOS){
14049 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14052 var v = this.inputEl().getValue();
14058 * Clears any text/value currently set in the field
14060 clearValue : function(){
14062 if(this.hiddenField){
14063 this.hiddenField.dom.value = '';
14066 this.setRawValue('');
14067 this.lastSelectionText = '';
14068 this.lastData = false;
14070 var close = this.closeTriggerEl();
14081 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14082 * will be displayed in the field. If the value does not match the data value of an existing item,
14083 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14084 * Otherwise the field will be blank (although the value will still be set).
14085 * @param {String} value The value to match
14087 setValue : function(v)
14089 if(Roo.isIOS && this.useNativeIOS){
14090 this.setIOSValue(v);
14100 if(this.valueField){
14101 var r = this.findRecord(this.valueField, v);
14103 text = r.data[this.displayField];
14104 }else if(this.valueNotFoundText !== undefined){
14105 text = this.valueNotFoundText;
14108 this.lastSelectionText = text;
14109 if(this.hiddenField){
14110 this.hiddenField.dom.value = v;
14112 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14115 var close = this.closeTriggerEl();
14118 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14124 * @property {Object} the last set data for the element
14129 * Sets the value of the field based on a object which is related to the record format for the store.
14130 * @param {Object} value the value to set as. or false on reset?
14132 setFromData : function(o){
14139 var dv = ''; // display value
14140 var vv = ''; // value value..
14142 if (this.displayField) {
14143 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14145 // this is an error condition!!!
14146 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14149 if(this.valueField){
14150 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14153 var close = this.closeTriggerEl();
14156 if(dv.length || vv * 1 > 0){
14158 this.blockFocus=true;
14164 if(this.hiddenField){
14165 this.hiddenField.dom.value = vv;
14167 this.lastSelectionText = dv;
14168 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14172 // no hidden field.. - we store the value in 'value', but still display
14173 // display field!!!!
14174 this.lastSelectionText = dv;
14175 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14182 reset : function(){
14183 // overridden so that last data is reset..
14190 this.setValue(this.originalValue);
14191 //this.clearInvalid();
14192 this.lastData = false;
14194 this.view.clearSelections();
14200 findRecord : function(prop, value){
14202 if(this.store.getCount() > 0){
14203 this.store.each(function(r){
14204 if(r.data[prop] == value){
14214 getName: function()
14216 // returns hidden if it's set..
14217 if (!this.rendered) {return ''};
14218 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14222 onViewMove : function(e, t){
14223 this.inKeyMode = false;
14227 onViewOver : function(e, t){
14228 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14231 var item = this.view.findItemFromChild(t);
14234 var index = this.view.indexOf(item);
14235 this.select(index, false);
14240 onViewClick : function(view, doFocus, el, e)
14242 var index = this.view.getSelectedIndexes()[0];
14244 var r = this.store.getAt(index);
14248 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14255 Roo.each(this.tickItems, function(v,k){
14257 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14259 _this.tickItems.splice(k, 1);
14261 if(typeof(e) == 'undefined' && view == false){
14262 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14274 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14275 this.tickItems.push(r.data);
14278 if(typeof(e) == 'undefined' && view == false){
14279 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14286 this.onSelect(r, index);
14288 if(doFocus !== false && !this.blockFocus){
14289 this.inputEl().focus();
14294 restrictHeight : function(){
14295 //this.innerList.dom.style.height = '';
14296 //var inner = this.innerList.dom;
14297 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14298 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14299 //this.list.beginUpdate();
14300 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14301 this.list.alignTo(this.inputEl(), this.listAlign);
14302 this.list.alignTo(this.inputEl(), this.listAlign);
14303 //this.list.endUpdate();
14307 onEmptyResults : function(){
14309 if(this.tickable && this.editable){
14310 this.hasFocus = false;
14311 this.restrictHeight();
14319 * Returns true if the dropdown list is expanded, else false.
14321 isExpanded : function(){
14322 return this.list.isVisible();
14326 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14327 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14328 * @param {String} value The data value of the item to select
14329 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14330 * selected item if it is not currently in view (defaults to true)
14331 * @return {Boolean} True if the value matched an item in the list, else false
14333 selectByValue : function(v, scrollIntoView){
14334 if(v !== undefined && v !== null){
14335 var r = this.findRecord(this.valueField || this.displayField, v);
14337 this.select(this.store.indexOf(r), scrollIntoView);
14345 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14346 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14347 * @param {Number} index The zero-based index of the list item to select
14348 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14349 * selected item if it is not currently in view (defaults to true)
14351 select : function(index, scrollIntoView){
14352 this.selectedIndex = index;
14353 this.view.select(index);
14354 if(scrollIntoView !== false){
14355 var el = this.view.getNode(index);
14357 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14360 this.list.scrollChildIntoView(el, false);
14366 selectNext : function(){
14367 var ct = this.store.getCount();
14369 if(this.selectedIndex == -1){
14371 }else if(this.selectedIndex < ct-1){
14372 this.select(this.selectedIndex+1);
14378 selectPrev : function(){
14379 var ct = this.store.getCount();
14381 if(this.selectedIndex == -1){
14383 }else if(this.selectedIndex != 0){
14384 this.select(this.selectedIndex-1);
14390 onKeyUp : function(e){
14391 if(this.editable !== false && !e.isSpecialKey()){
14392 this.lastKey = e.getKey();
14393 this.dqTask.delay(this.queryDelay);
14398 validateBlur : function(){
14399 return !this.list || !this.list.isVisible();
14403 initQuery : function(){
14405 var v = this.getRawValue();
14407 if(this.tickable && this.editable){
14408 v = this.tickableInputEl().getValue();
14415 doForce : function(){
14416 if(this.inputEl().dom.value.length > 0){
14417 this.inputEl().dom.value =
14418 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14424 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14425 * query allowing the query action to be canceled if needed.
14426 * @param {String} query The SQL query to execute
14427 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14428 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14429 * saved in the current store (defaults to false)
14431 doQuery : function(q, forceAll){
14433 if(q === undefined || q === null){
14438 forceAll: forceAll,
14442 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14447 forceAll = qe.forceAll;
14448 if(forceAll === true || (q.length >= this.minChars)){
14450 this.hasQuery = true;
14452 if(this.lastQuery != q || this.alwaysQuery){
14453 this.lastQuery = q;
14454 if(this.mode == 'local'){
14455 this.selectedIndex = -1;
14457 this.store.clearFilter();
14460 if(this.specialFilter){
14461 this.fireEvent('specialfilter', this);
14466 this.store.filter(this.displayField, q);
14469 this.store.fireEvent("datachanged", this.store);
14476 this.store.baseParams[this.queryParam] = q;
14478 var options = {params : this.getParams(q)};
14481 options.add = true;
14482 options.params.start = this.page * this.pageSize;
14485 this.store.load(options);
14488 * this code will make the page width larger, at the beginning, the list not align correctly,
14489 * we should expand the list on onLoad
14490 * so command out it
14495 this.selectedIndex = -1;
14500 this.loadNext = false;
14504 getParams : function(q){
14506 //p[this.queryParam] = q;
14510 p.limit = this.pageSize;
14516 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14518 collapse : function(){
14519 if(!this.isExpanded()){
14525 this.hasFocus = false;
14529 this.cancelBtn.hide();
14530 this.trigger.show();
14533 this.tickableInputEl().dom.value = '';
14534 this.tickableInputEl().blur();
14539 Roo.get(document).un('mousedown', this.collapseIf, this);
14540 Roo.get(document).un('mousewheel', this.collapseIf, this);
14541 if (!this.editable) {
14542 Roo.get(document).un('keydown', this.listKeyPress, this);
14544 this.fireEvent('collapse', this);
14550 collapseIf : function(e){
14551 var in_combo = e.within(this.el);
14552 var in_list = e.within(this.list);
14553 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14555 if (in_combo || in_list || is_list) {
14556 //e.stopPropagation();
14561 this.onTickableFooterButtonClick(e, false, false);
14569 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14571 expand : function(){
14573 if(this.isExpanded() || !this.hasFocus){
14577 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14578 this.list.setWidth(lw);
14584 this.restrictHeight();
14588 this.tickItems = Roo.apply([], this.item);
14591 this.cancelBtn.show();
14592 this.trigger.hide();
14595 this.tickableInputEl().focus();
14600 Roo.get(document).on('mousedown', this.collapseIf, this);
14601 Roo.get(document).on('mousewheel', this.collapseIf, this);
14602 if (!this.editable) {
14603 Roo.get(document).on('keydown', this.listKeyPress, this);
14606 this.fireEvent('expand', this);
14610 // Implements the default empty TriggerField.onTriggerClick function
14611 onTriggerClick : function(e)
14613 Roo.log('trigger click');
14615 if(this.disabled || !this.triggerList){
14620 this.loadNext = false;
14622 if(this.isExpanded()){
14624 if (!this.blockFocus) {
14625 this.inputEl().focus();
14629 this.hasFocus = true;
14630 if(this.triggerAction == 'all') {
14631 this.doQuery(this.allQuery, true);
14633 this.doQuery(this.getRawValue());
14635 if (!this.blockFocus) {
14636 this.inputEl().focus();
14641 onTickableTriggerClick : function(e)
14648 this.loadNext = false;
14649 this.hasFocus = true;
14651 if(this.triggerAction == 'all') {
14652 this.doQuery(this.allQuery, true);
14654 this.doQuery(this.getRawValue());
14658 onSearchFieldClick : function(e)
14660 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14661 this.onTickableFooterButtonClick(e, false, false);
14665 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14670 this.loadNext = false;
14671 this.hasFocus = true;
14673 if(this.triggerAction == 'all') {
14674 this.doQuery(this.allQuery, true);
14676 this.doQuery(this.getRawValue());
14680 listKeyPress : function(e)
14682 //Roo.log('listkeypress');
14683 // scroll to first matching element based on key pres..
14684 if (e.isSpecialKey()) {
14687 var k = String.fromCharCode(e.getKey()).toUpperCase();
14690 var csel = this.view.getSelectedNodes();
14691 var cselitem = false;
14693 var ix = this.view.indexOf(csel[0]);
14694 cselitem = this.store.getAt(ix);
14695 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14701 this.store.each(function(v) {
14703 // start at existing selection.
14704 if (cselitem.id == v.id) {
14710 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14711 match = this.store.indexOf(v);
14717 if (match === false) {
14718 return true; // no more action?
14721 this.view.select(match);
14722 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14723 sn.scrollIntoView(sn.dom.parentNode, false);
14726 onViewScroll : function(e, t){
14728 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){
14732 this.hasQuery = true;
14734 this.loading = this.list.select('.loading', true).first();
14736 if(this.loading === null){
14737 this.list.createChild({
14739 cls: 'loading roo-select2-more-results roo-select2-active',
14740 html: 'Loading more results...'
14743 this.loading = this.list.select('.loading', true).first();
14745 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14747 this.loading.hide();
14750 this.loading.show();
14755 this.loadNext = true;
14757 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14762 addItem : function(o)
14764 var dv = ''; // display value
14766 if (this.displayField) {
14767 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14769 // this is an error condition!!!
14770 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14777 var choice = this.choices.createChild({
14779 cls: 'roo-select2-search-choice',
14788 cls: 'roo-select2-search-choice-close fa fa-times',
14793 }, this.searchField);
14795 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14797 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14805 this.inputEl().dom.value = '';
14810 onRemoveItem : function(e, _self, o)
14812 e.preventDefault();
14814 this.lastItem = Roo.apply([], this.item);
14816 var index = this.item.indexOf(o.data) * 1;
14819 Roo.log('not this item?!');
14823 this.item.splice(index, 1);
14828 this.fireEvent('remove', this, e);
14834 syncValue : function()
14836 if(!this.item.length){
14843 Roo.each(this.item, function(i){
14844 if(_this.valueField){
14845 value.push(i[_this.valueField]);
14852 this.value = value.join(',');
14854 if(this.hiddenField){
14855 this.hiddenField.dom.value = this.value;
14858 this.store.fireEvent("datachanged", this.store);
14863 clearItem : function()
14865 if(!this.multiple){
14871 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14879 if(this.tickable && !Roo.isTouch){
14880 this.view.refresh();
14884 inputEl: function ()
14886 if(Roo.isIOS && this.useNativeIOS){
14887 return this.el.select('select.roo-ios-select', true).first();
14890 if(Roo.isTouch && this.mobileTouchView){
14891 return this.el.select('input.form-control',true).first();
14895 return this.searchField;
14898 return this.el.select('input.form-control',true).first();
14901 onTickableFooterButtonClick : function(e, btn, el)
14903 e.preventDefault();
14905 this.lastItem = Roo.apply([], this.item);
14907 if(btn && btn.name == 'cancel'){
14908 this.tickItems = Roo.apply([], this.item);
14917 Roo.each(this.tickItems, function(o){
14925 validate : function()
14927 if(this.getVisibilityEl().hasClass('hidden')){
14931 var v = this.getRawValue();
14934 v = this.getValue();
14937 if(this.disabled || this.allowBlank || v.length){
14942 this.markInvalid();
14946 tickableInputEl : function()
14948 if(!this.tickable || !this.editable){
14949 return this.inputEl();
14952 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14956 getAutoCreateTouchView : function()
14961 cls: 'form-group' //input-group
14967 type : this.inputType,
14968 cls : 'form-control x-combo-noedit',
14969 autocomplete: 'new-password',
14970 placeholder : this.placeholder || '',
14975 input.name = this.name;
14979 input.cls += ' input-' + this.size;
14982 if (this.disabled) {
14983 input.disabled = true;
14994 inputblock.cls += ' input-group';
14996 inputblock.cn.unshift({
14998 cls : 'input-group-addon',
15003 if(this.removable && !this.multiple){
15004 inputblock.cls += ' roo-removable';
15006 inputblock.cn.push({
15009 cls : 'roo-combo-removable-btn close'
15013 if(this.hasFeedback && !this.allowBlank){
15015 inputblock.cls += ' has-feedback';
15017 inputblock.cn.push({
15019 cls: 'glyphicon form-control-feedback'
15026 inputblock.cls += (this.before) ? '' : ' input-group';
15028 inputblock.cn.push({
15030 cls : 'input-group-addon',
15041 cls: 'form-hidden-field'
15055 cls: 'form-hidden-field'
15059 cls: 'roo-select2-choices',
15063 cls: 'roo-select2-search-field',
15076 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15082 if(!this.multiple && this.showToggleBtn){
15089 if (this.caret != false) {
15092 cls: 'fa fa-' + this.caret
15099 cls : 'input-group-addon btn dropdown-toggle',
15104 cls: 'combobox-clear',
15118 combobox.cls += ' roo-select2-container-multi';
15121 var align = this.labelAlign || this.parentLabelAlign();
15123 if (align ==='left' && this.fieldLabel.length) {
15128 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15129 tooltip : 'This field is required'
15133 cls : 'control-label',
15134 html : this.fieldLabel
15145 var labelCfg = cfg.cn[1];
15146 var contentCfg = cfg.cn[2];
15149 if(this.indicatorpos == 'right'){
15154 cls : 'control-label',
15158 html : this.fieldLabel
15162 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15163 tooltip : 'This field is required'
15176 labelCfg = cfg.cn[0];
15177 contentCfg = cfg.cn[1];
15182 if(this.labelWidth > 12){
15183 labelCfg.style = "width: " + this.labelWidth + 'px';
15186 if(this.labelWidth < 13 && this.labelmd == 0){
15187 this.labelmd = this.labelWidth;
15190 if(this.labellg > 0){
15191 labelCfg.cls += ' col-lg-' + this.labellg;
15192 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15195 if(this.labelmd > 0){
15196 labelCfg.cls += ' col-md-' + this.labelmd;
15197 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15200 if(this.labelsm > 0){
15201 labelCfg.cls += ' col-sm-' + this.labelsm;
15202 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15205 if(this.labelxs > 0){
15206 labelCfg.cls += ' col-xs-' + this.labelxs;
15207 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15211 } else if ( this.fieldLabel.length) {
15215 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15216 tooltip : 'This field is required'
15220 cls : 'control-label',
15221 html : this.fieldLabel
15232 if(this.indicatorpos == 'right'){
15236 cls : 'control-label',
15237 html : this.fieldLabel,
15241 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15242 tooltip : 'This field is required'
15259 var settings = this;
15261 ['xs','sm','md','lg'].map(function(size){
15262 if (settings[size]) {
15263 cfg.cls += ' col-' + size + '-' + settings[size];
15270 initTouchView : function()
15272 this.renderTouchView();
15274 this.touchViewEl.on('scroll', function(){
15275 this.el.dom.scrollTop = 0;
15278 this.originalValue = this.getValue();
15280 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15282 this.inputEl().on("click", this.showTouchView, this);
15283 if (this.triggerEl) {
15284 this.triggerEl.on("click", this.showTouchView, this);
15288 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15289 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15291 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15293 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15294 this.store.on('load', this.onTouchViewLoad, this);
15295 this.store.on('loadexception', this.onTouchViewLoadException, this);
15297 if(this.hiddenName){
15299 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15301 this.hiddenField.dom.value =
15302 this.hiddenValue !== undefined ? this.hiddenValue :
15303 this.value !== undefined ? this.value : '';
15305 this.el.dom.removeAttribute('name');
15306 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15310 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15311 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15314 if(this.removable && !this.multiple){
15315 var close = this.closeTriggerEl();
15317 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15318 close.on('click', this.removeBtnClick, this, close);
15322 * fix the bug in Safari iOS8
15324 this.inputEl().on("focus", function(e){
15325 document.activeElement.blur();
15328 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15335 renderTouchView : function()
15337 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15338 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15340 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15341 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15343 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15344 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15345 this.touchViewBodyEl.setStyle('overflow', 'auto');
15347 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15348 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15350 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15351 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15355 showTouchView : function()
15361 this.touchViewHeaderEl.hide();
15363 if(this.modalTitle.length){
15364 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15365 this.touchViewHeaderEl.show();
15368 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15369 this.touchViewEl.show();
15371 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15373 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15374 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15376 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15378 if(this.modalTitle.length){
15379 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15382 this.touchViewBodyEl.setHeight(bodyHeight);
15386 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15388 this.touchViewEl.addClass('in');
15391 if(this._touchViewMask){
15392 Roo.get(document.body).addClass("x-body-masked");
15393 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15394 this._touchViewMask.setStyle('z-index', 10000);
15395 this._touchViewMask.addClass('show');
15398 this.doTouchViewQuery();
15402 hideTouchView : function()
15404 this.touchViewEl.removeClass('in');
15408 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15410 this.touchViewEl.setStyle('display', 'none');
15413 if(this._touchViewMask){
15414 this._touchViewMask.removeClass('show');
15415 Roo.get(document.body).removeClass("x-body-masked");
15419 setTouchViewValue : function()
15426 Roo.each(this.tickItems, function(o){
15431 this.hideTouchView();
15434 doTouchViewQuery : function()
15443 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15447 if(!this.alwaysQuery || this.mode == 'local'){
15448 this.onTouchViewLoad();
15455 onTouchViewBeforeLoad : function(combo,opts)
15461 onTouchViewLoad : function()
15463 if(this.store.getCount() < 1){
15464 this.onTouchViewEmptyResults();
15468 this.clearTouchView();
15470 var rawValue = this.getRawValue();
15472 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15474 this.tickItems = [];
15476 this.store.data.each(function(d, rowIndex){
15477 var row = this.touchViewListGroup.createChild(template);
15479 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15480 row.addClass(d.data.cls);
15483 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15486 html : d.data[this.displayField]
15489 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15490 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15493 row.removeClass('selected');
15494 if(!this.multiple && this.valueField &&
15495 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15498 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15499 row.addClass('selected');
15502 if(this.multiple && this.valueField &&
15503 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15507 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15508 this.tickItems.push(d.data);
15511 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15515 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15517 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15519 if(this.modalTitle.length){
15520 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15523 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15525 if(this.mobile_restrict_height && listHeight < bodyHeight){
15526 this.touchViewBodyEl.setHeight(listHeight);
15531 if(firstChecked && listHeight > bodyHeight){
15532 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15537 onTouchViewLoadException : function()
15539 this.hideTouchView();
15542 onTouchViewEmptyResults : function()
15544 this.clearTouchView();
15546 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15548 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15552 clearTouchView : function()
15554 this.touchViewListGroup.dom.innerHTML = '';
15557 onTouchViewClick : function(e, el, o)
15559 e.preventDefault();
15562 var rowIndex = o.rowIndex;
15564 var r = this.store.getAt(rowIndex);
15566 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15568 if(!this.multiple){
15569 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15570 c.dom.removeAttribute('checked');
15573 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15575 this.setFromData(r.data);
15577 var close = this.closeTriggerEl();
15583 this.hideTouchView();
15585 this.fireEvent('select', this, r, rowIndex);
15590 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15591 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15592 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15596 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15597 this.addItem(r.data);
15598 this.tickItems.push(r.data);
15602 getAutoCreateNativeIOS : function()
15605 cls: 'form-group' //input-group,
15610 cls : 'roo-ios-select'
15614 combobox.name = this.name;
15617 if (this.disabled) {
15618 combobox.disabled = true;
15621 var settings = this;
15623 ['xs','sm','md','lg'].map(function(size){
15624 if (settings[size]) {
15625 cfg.cls += ' col-' + size + '-' + settings[size];
15635 initIOSView : function()
15637 this.store.on('load', this.onIOSViewLoad, this);
15642 onIOSViewLoad : function()
15644 if(this.store.getCount() < 1){
15648 this.clearIOSView();
15650 if(this.allowBlank) {
15652 var default_text = '-- SELECT --';
15654 if(this.placeholder.length){
15655 default_text = this.placeholder;
15658 if(this.emptyTitle.length){
15659 default_text += ' - ' + this.emptyTitle + ' -';
15662 var opt = this.inputEl().createChild({
15665 html : default_text
15669 o[this.valueField] = 0;
15670 o[this.displayField] = default_text;
15672 this.ios_options.push({
15679 this.store.data.each(function(d, rowIndex){
15683 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15684 html = d.data[this.displayField];
15689 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15690 value = d.data[this.valueField];
15699 if(this.value == d.data[this.valueField]){
15700 option['selected'] = true;
15703 var opt = this.inputEl().createChild(option);
15705 this.ios_options.push({
15712 this.inputEl().on('change', function(){
15713 this.fireEvent('select', this);
15718 clearIOSView: function()
15720 this.inputEl().dom.innerHTML = '';
15722 this.ios_options = [];
15725 setIOSValue: function(v)
15729 if(!this.ios_options){
15733 Roo.each(this.ios_options, function(opts){
15735 opts.el.dom.removeAttribute('selected');
15737 if(opts.data[this.valueField] != v){
15741 opts.el.dom.setAttribute('selected', true);
15747 * @cfg {Boolean} grow
15751 * @cfg {Number} growMin
15755 * @cfg {Number} growMax
15764 Roo.apply(Roo.bootstrap.ComboBox, {
15768 cls: 'modal-header',
15790 cls: 'list-group-item',
15794 cls: 'roo-combobox-list-group-item-value'
15798 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15812 listItemCheckbox : {
15814 cls: 'list-group-item',
15818 cls: 'roo-combobox-list-group-item-value'
15822 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15838 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15843 cls: 'modal-footer',
15851 cls: 'col-xs-6 text-left',
15854 cls: 'btn btn-danger roo-touch-view-cancel',
15860 cls: 'col-xs-6 text-right',
15863 cls: 'btn btn-success roo-touch-view-ok',
15874 Roo.apply(Roo.bootstrap.ComboBox, {
15876 touchViewTemplate : {
15878 cls: 'modal fade roo-combobox-touch-view',
15882 cls: 'modal-dialog',
15883 style : 'position:fixed', // we have to fix position....
15887 cls: 'modal-content',
15889 Roo.bootstrap.ComboBox.header,
15890 Roo.bootstrap.ComboBox.body,
15891 Roo.bootstrap.ComboBox.footer
15900 * Ext JS Library 1.1.1
15901 * Copyright(c) 2006-2007, Ext JS, LLC.
15903 * Originally Released Under LGPL - original licence link has changed is not relivant.
15906 * <script type="text/javascript">
15911 * @extends Roo.util.Observable
15912 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15913 * This class also supports single and multi selection modes. <br>
15914 * Create a data model bound view:
15916 var store = new Roo.data.Store(...);
15918 var view = new Roo.View({
15920 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15922 singleSelect: true,
15923 selectedClass: "ydataview-selected",
15927 // listen for node click?
15928 view.on("click", function(vw, index, node, e){
15929 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15933 dataModel.load("foobar.xml");
15935 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15937 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15938 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15940 * Note: old style constructor is still suported (container, template, config)
15943 * Create a new View
15944 * @param {Object} config The config object
15947 Roo.View = function(config, depreciated_tpl, depreciated_config){
15949 this.parent = false;
15951 if (typeof(depreciated_tpl) == 'undefined') {
15952 // new way.. - universal constructor.
15953 Roo.apply(this, config);
15954 this.el = Roo.get(this.el);
15957 this.el = Roo.get(config);
15958 this.tpl = depreciated_tpl;
15959 Roo.apply(this, depreciated_config);
15961 this.wrapEl = this.el.wrap().wrap();
15962 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15965 if(typeof(this.tpl) == "string"){
15966 this.tpl = new Roo.Template(this.tpl);
15968 // support xtype ctors..
15969 this.tpl = new Roo.factory(this.tpl, Roo);
15973 this.tpl.compile();
15978 * @event beforeclick
15979 * Fires before a click is processed. Returns false to cancel the default action.
15980 * @param {Roo.View} this
15981 * @param {Number} index The index of the target node
15982 * @param {HTMLElement} node The target node
15983 * @param {Roo.EventObject} e The raw event object
15985 "beforeclick" : true,
15988 * Fires when a template node is clicked.
15989 * @param {Roo.View} this
15990 * @param {Number} index The index of the target node
15991 * @param {HTMLElement} node The target node
15992 * @param {Roo.EventObject} e The raw event object
15997 * Fires when a template node is double clicked.
15998 * @param {Roo.View} this
15999 * @param {Number} index The index of the target node
16000 * @param {HTMLElement} node The target node
16001 * @param {Roo.EventObject} e The raw event object
16005 * @event contextmenu
16006 * Fires when a template node is right clicked.
16007 * @param {Roo.View} this
16008 * @param {Number} index The index of the target node
16009 * @param {HTMLElement} node The target node
16010 * @param {Roo.EventObject} e The raw event object
16012 "contextmenu" : true,
16014 * @event selectionchange
16015 * Fires when the selected nodes change.
16016 * @param {Roo.View} this
16017 * @param {Array} selections Array of the selected nodes
16019 "selectionchange" : true,
16022 * @event beforeselect
16023 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16024 * @param {Roo.View} this
16025 * @param {HTMLElement} node The node to be selected
16026 * @param {Array} selections Array of currently selected nodes
16028 "beforeselect" : true,
16030 * @event preparedata
16031 * Fires on every row to render, to allow you to change the data.
16032 * @param {Roo.View} this
16033 * @param {Object} data to be rendered (change this)
16035 "preparedata" : true
16043 "click": this.onClick,
16044 "dblclick": this.onDblClick,
16045 "contextmenu": this.onContextMenu,
16049 this.selections = [];
16051 this.cmp = new Roo.CompositeElementLite([]);
16053 this.store = Roo.factory(this.store, Roo.data);
16054 this.setStore(this.store, true);
16057 if ( this.footer && this.footer.xtype) {
16059 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16061 this.footer.dataSource = this.store;
16062 this.footer.container = fctr;
16063 this.footer = Roo.factory(this.footer, Roo);
16064 fctr.insertFirst(this.el);
16066 // this is a bit insane - as the paging toolbar seems to detach the el..
16067 // dom.parentNode.parentNode.parentNode
16068 // they get detached?
16072 Roo.View.superclass.constructor.call(this);
16077 Roo.extend(Roo.View, Roo.util.Observable, {
16080 * @cfg {Roo.data.Store} store Data store to load data from.
16085 * @cfg {String|Roo.Element} el The container element.
16090 * @cfg {String|Roo.Template} tpl The template used by this View
16094 * @cfg {String} dataName the named area of the template to use as the data area
16095 * Works with domtemplates roo-name="name"
16099 * @cfg {String} selectedClass The css class to add to selected nodes
16101 selectedClass : "x-view-selected",
16103 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16108 * @cfg {String} text to display on mask (default Loading)
16112 * @cfg {Boolean} multiSelect Allow multiple selection
16114 multiSelect : false,
16116 * @cfg {Boolean} singleSelect Allow single selection
16118 singleSelect: false,
16121 * @cfg {Boolean} toggleSelect - selecting
16123 toggleSelect : false,
16126 * @cfg {Boolean} tickable - selecting
16131 * Returns the element this view is bound to.
16132 * @return {Roo.Element}
16134 getEl : function(){
16135 return this.wrapEl;
16141 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16143 refresh : function(){
16144 //Roo.log('refresh');
16147 // if we are using something like 'domtemplate', then
16148 // the what gets used is:
16149 // t.applySubtemplate(NAME, data, wrapping data..)
16150 // the outer template then get' applied with
16151 // the store 'extra data'
16152 // and the body get's added to the
16153 // roo-name="data" node?
16154 // <span class='roo-tpl-{name}'></span> ?????
16158 this.clearSelections();
16159 this.el.update("");
16161 var records = this.store.getRange();
16162 if(records.length < 1) {
16164 // is this valid?? = should it render a template??
16166 this.el.update(this.emptyText);
16170 if (this.dataName) {
16171 this.el.update(t.apply(this.store.meta)); //????
16172 el = this.el.child('.roo-tpl-' + this.dataName);
16175 for(var i = 0, len = records.length; i < len; i++){
16176 var data = this.prepareData(records[i].data, i, records[i]);
16177 this.fireEvent("preparedata", this, data, i, records[i]);
16179 var d = Roo.apply({}, data);
16182 Roo.apply(d, {'roo-id' : Roo.id()});
16186 Roo.each(this.parent.item, function(item){
16187 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16190 Roo.apply(d, {'roo-data-checked' : 'checked'});
16194 html[html.length] = Roo.util.Format.trim(
16196 t.applySubtemplate(this.dataName, d, this.store.meta) :
16203 el.update(html.join(""));
16204 this.nodes = el.dom.childNodes;
16205 this.updateIndexes(0);
16210 * Function to override to reformat the data that is sent to
16211 * the template for each node.
16212 * DEPRICATED - use the preparedata event handler.
16213 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16214 * a JSON object for an UpdateManager bound view).
16216 prepareData : function(data, index, record)
16218 this.fireEvent("preparedata", this, data, index, record);
16222 onUpdate : function(ds, record){
16223 // Roo.log('on update');
16224 this.clearSelections();
16225 var index = this.store.indexOf(record);
16226 var n = this.nodes[index];
16227 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16228 n.parentNode.removeChild(n);
16229 this.updateIndexes(index, index);
16235 onAdd : function(ds, records, index)
16237 //Roo.log(['on Add', ds, records, index] );
16238 this.clearSelections();
16239 if(this.nodes.length == 0){
16243 var n = this.nodes[index];
16244 for(var i = 0, len = records.length; i < len; i++){
16245 var d = this.prepareData(records[i].data, i, records[i]);
16247 this.tpl.insertBefore(n, d);
16250 this.tpl.append(this.el, d);
16253 this.updateIndexes(index);
16256 onRemove : function(ds, record, index){
16257 // Roo.log('onRemove');
16258 this.clearSelections();
16259 var el = this.dataName ?
16260 this.el.child('.roo-tpl-' + this.dataName) :
16263 el.dom.removeChild(this.nodes[index]);
16264 this.updateIndexes(index);
16268 * Refresh an individual node.
16269 * @param {Number} index
16271 refreshNode : function(index){
16272 this.onUpdate(this.store, this.store.getAt(index));
16275 updateIndexes : function(startIndex, endIndex){
16276 var ns = this.nodes;
16277 startIndex = startIndex || 0;
16278 endIndex = endIndex || ns.length - 1;
16279 for(var i = startIndex; i <= endIndex; i++){
16280 ns[i].nodeIndex = i;
16285 * Changes the data store this view uses and refresh the view.
16286 * @param {Store} store
16288 setStore : function(store, initial){
16289 if(!initial && this.store){
16290 this.store.un("datachanged", this.refresh);
16291 this.store.un("add", this.onAdd);
16292 this.store.un("remove", this.onRemove);
16293 this.store.un("update", this.onUpdate);
16294 this.store.un("clear", this.refresh);
16295 this.store.un("beforeload", this.onBeforeLoad);
16296 this.store.un("load", this.onLoad);
16297 this.store.un("loadexception", this.onLoad);
16301 store.on("datachanged", this.refresh, this);
16302 store.on("add", this.onAdd, this);
16303 store.on("remove", this.onRemove, this);
16304 store.on("update", this.onUpdate, this);
16305 store.on("clear", this.refresh, this);
16306 store.on("beforeload", this.onBeforeLoad, this);
16307 store.on("load", this.onLoad, this);
16308 store.on("loadexception", this.onLoad, this);
16316 * onbeforeLoad - masks the loading area.
16319 onBeforeLoad : function(store,opts)
16321 //Roo.log('onBeforeLoad');
16323 this.el.update("");
16325 this.el.mask(this.mask ? this.mask : "Loading" );
16327 onLoad : function ()
16334 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16335 * @param {HTMLElement} node
16336 * @return {HTMLElement} The template node
16338 findItemFromChild : function(node){
16339 var el = this.dataName ?
16340 this.el.child('.roo-tpl-' + this.dataName,true) :
16343 if(!node || node.parentNode == el){
16346 var p = node.parentNode;
16347 while(p && p != el){
16348 if(p.parentNode == el){
16357 onClick : function(e){
16358 var item = this.findItemFromChild(e.getTarget());
16360 var index = this.indexOf(item);
16361 if(this.onItemClick(item, index, e) !== false){
16362 this.fireEvent("click", this, index, item, e);
16365 this.clearSelections();
16370 onContextMenu : function(e){
16371 var item = this.findItemFromChild(e.getTarget());
16373 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16378 onDblClick : function(e){
16379 var item = this.findItemFromChild(e.getTarget());
16381 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16385 onItemClick : function(item, index, e)
16387 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16390 if (this.toggleSelect) {
16391 var m = this.isSelected(item) ? 'unselect' : 'select';
16394 _t[m](item, true, false);
16397 if(this.multiSelect || this.singleSelect){
16398 if(this.multiSelect && e.shiftKey && this.lastSelection){
16399 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16401 this.select(item, this.multiSelect && e.ctrlKey);
16402 this.lastSelection = item;
16405 if(!this.tickable){
16406 e.preventDefault();
16414 * Get the number of selected nodes.
16417 getSelectionCount : function(){
16418 return this.selections.length;
16422 * Get the currently selected nodes.
16423 * @return {Array} An array of HTMLElements
16425 getSelectedNodes : function(){
16426 return this.selections;
16430 * Get the indexes of the selected nodes.
16433 getSelectedIndexes : function(){
16434 var indexes = [], s = this.selections;
16435 for(var i = 0, len = s.length; i < len; i++){
16436 indexes.push(s[i].nodeIndex);
16442 * Clear all selections
16443 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16445 clearSelections : function(suppressEvent){
16446 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16447 this.cmp.elements = this.selections;
16448 this.cmp.removeClass(this.selectedClass);
16449 this.selections = [];
16450 if(!suppressEvent){
16451 this.fireEvent("selectionchange", this, this.selections);
16457 * Returns true if the passed node is selected
16458 * @param {HTMLElement/Number} node The node or node index
16459 * @return {Boolean}
16461 isSelected : function(node){
16462 var s = this.selections;
16466 node = this.getNode(node);
16467 return s.indexOf(node) !== -1;
16472 * @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
16473 * @param {Boolean} keepExisting (optional) true to keep existing selections
16474 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16476 select : function(nodeInfo, keepExisting, suppressEvent){
16477 if(nodeInfo instanceof Array){
16479 this.clearSelections(true);
16481 for(var i = 0, len = nodeInfo.length; i < len; i++){
16482 this.select(nodeInfo[i], true, true);
16486 var node = this.getNode(nodeInfo);
16487 if(!node || this.isSelected(node)){
16488 return; // already selected.
16491 this.clearSelections(true);
16494 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16495 Roo.fly(node).addClass(this.selectedClass);
16496 this.selections.push(node);
16497 if(!suppressEvent){
16498 this.fireEvent("selectionchange", this, this.selections);
16506 * @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
16507 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16508 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16510 unselect : function(nodeInfo, keepExisting, suppressEvent)
16512 if(nodeInfo instanceof Array){
16513 Roo.each(this.selections, function(s) {
16514 this.unselect(s, nodeInfo);
16518 var node = this.getNode(nodeInfo);
16519 if(!node || !this.isSelected(node)){
16520 //Roo.log("not selected");
16521 return; // not selected.
16525 Roo.each(this.selections, function(s) {
16527 Roo.fly(node).removeClass(this.selectedClass);
16534 this.selections= ns;
16535 this.fireEvent("selectionchange", this, this.selections);
16539 * Gets a template node.
16540 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16541 * @return {HTMLElement} The node or null if it wasn't found
16543 getNode : function(nodeInfo){
16544 if(typeof nodeInfo == "string"){
16545 return document.getElementById(nodeInfo);
16546 }else if(typeof nodeInfo == "number"){
16547 return this.nodes[nodeInfo];
16553 * Gets a range template nodes.
16554 * @param {Number} startIndex
16555 * @param {Number} endIndex
16556 * @return {Array} An array of nodes
16558 getNodes : function(start, end){
16559 var ns = this.nodes;
16560 start = start || 0;
16561 end = typeof end == "undefined" ? ns.length - 1 : end;
16564 for(var i = start; i <= end; i++){
16568 for(var i = start; i >= end; i--){
16576 * Finds the index of the passed node
16577 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16578 * @return {Number} The index of the node or -1
16580 indexOf : function(node){
16581 node = this.getNode(node);
16582 if(typeof node.nodeIndex == "number"){
16583 return node.nodeIndex;
16585 var ns = this.nodes;
16586 for(var i = 0, len = ns.length; i < len; i++){
16597 * based on jquery fullcalendar
16601 Roo.bootstrap = Roo.bootstrap || {};
16603 * @class Roo.bootstrap.Calendar
16604 * @extends Roo.bootstrap.Component
16605 * Bootstrap Calendar class
16606 * @cfg {Boolean} loadMask (true|false) default false
16607 * @cfg {Object} header generate the user specific header of the calendar, default false
16610 * Create a new Container
16611 * @param {Object} config The config object
16616 Roo.bootstrap.Calendar = function(config){
16617 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16621 * Fires when a date is selected
16622 * @param {DatePicker} this
16623 * @param {Date} date The selected date
16627 * @event monthchange
16628 * Fires when the displayed month changes
16629 * @param {DatePicker} this
16630 * @param {Date} date The selected month
16632 'monthchange': true,
16634 * @event evententer
16635 * Fires when mouse over an event
16636 * @param {Calendar} this
16637 * @param {event} Event
16639 'evententer': true,
16641 * @event eventleave
16642 * Fires when the mouse leaves an
16643 * @param {Calendar} this
16646 'eventleave': true,
16648 * @event eventclick
16649 * Fires when the mouse click an
16650 * @param {Calendar} this
16659 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16662 * @cfg {Number} startDay
16663 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16671 getAutoCreate : function(){
16674 var fc_button = function(name, corner, style, content ) {
16675 return Roo.apply({},{
16677 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16679 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16682 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16693 style : 'width:100%',
16700 cls : 'fc-header-left',
16702 fc_button('prev', 'left', 'arrow', '‹' ),
16703 fc_button('next', 'right', 'arrow', '›' ),
16704 { tag: 'span', cls: 'fc-header-space' },
16705 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16713 cls : 'fc-header-center',
16717 cls: 'fc-header-title',
16720 html : 'month / year'
16728 cls : 'fc-header-right',
16730 /* fc_button('month', 'left', '', 'month' ),
16731 fc_button('week', '', '', 'week' ),
16732 fc_button('day', 'right', '', 'day' )
16744 header = this.header;
16747 var cal_heads = function() {
16749 // fixme - handle this.
16751 for (var i =0; i < Date.dayNames.length; i++) {
16752 var d = Date.dayNames[i];
16755 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16756 html : d.substring(0,3)
16760 ret[0].cls += ' fc-first';
16761 ret[6].cls += ' fc-last';
16764 var cal_cell = function(n) {
16767 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16772 cls: 'fc-day-number',
16776 cls: 'fc-day-content',
16780 style: 'position: relative;' // height: 17px;
16792 var cal_rows = function() {
16795 for (var r = 0; r < 6; r++) {
16802 for (var i =0; i < Date.dayNames.length; i++) {
16803 var d = Date.dayNames[i];
16804 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16807 row.cn[0].cls+=' fc-first';
16808 row.cn[0].cn[0].style = 'min-height:90px';
16809 row.cn[6].cls+=' fc-last';
16813 ret[0].cls += ' fc-first';
16814 ret[4].cls += ' fc-prev-last';
16815 ret[5].cls += ' fc-last';
16822 cls: 'fc-border-separate',
16823 style : 'width:100%',
16831 cls : 'fc-first fc-last',
16849 cls : 'fc-content',
16850 style : "position: relative;",
16853 cls : 'fc-view fc-view-month fc-grid',
16854 style : 'position: relative',
16855 unselectable : 'on',
16858 cls : 'fc-event-container',
16859 style : 'position:absolute;z-index:8;top:0;left:0;'
16877 initEvents : function()
16880 throw "can not find store for calendar";
16886 style: "text-align:center",
16890 style: "background-color:white;width:50%;margin:250 auto",
16894 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16905 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16907 var size = this.el.select('.fc-content', true).first().getSize();
16908 this.maskEl.setSize(size.width, size.height);
16909 this.maskEl.enableDisplayMode("block");
16910 if(!this.loadMask){
16911 this.maskEl.hide();
16914 this.store = Roo.factory(this.store, Roo.data);
16915 this.store.on('load', this.onLoad, this);
16916 this.store.on('beforeload', this.onBeforeLoad, this);
16920 this.cells = this.el.select('.fc-day',true);
16921 //Roo.log(this.cells);
16922 this.textNodes = this.el.query('.fc-day-number');
16923 this.cells.addClassOnOver('fc-state-hover');
16925 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16926 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16927 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16928 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16930 this.on('monthchange', this.onMonthChange, this);
16932 this.update(new Date().clearTime());
16935 resize : function() {
16936 var sz = this.el.getSize();
16938 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16939 this.el.select('.fc-day-content div',true).setHeight(34);
16944 showPrevMonth : function(e){
16945 this.update(this.activeDate.add("mo", -1));
16947 showToday : function(e){
16948 this.update(new Date().clearTime());
16951 showNextMonth : function(e){
16952 this.update(this.activeDate.add("mo", 1));
16956 showPrevYear : function(){
16957 this.update(this.activeDate.add("y", -1));
16961 showNextYear : function(){
16962 this.update(this.activeDate.add("y", 1));
16967 update : function(date)
16969 var vd = this.activeDate;
16970 this.activeDate = date;
16971 // if(vd && this.el){
16972 // var t = date.getTime();
16973 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16974 // Roo.log('using add remove');
16976 // this.fireEvent('monthchange', this, date);
16978 // this.cells.removeClass("fc-state-highlight");
16979 // this.cells.each(function(c){
16980 // if(c.dateValue == t){
16981 // c.addClass("fc-state-highlight");
16982 // setTimeout(function(){
16983 // try{c.dom.firstChild.focus();}catch(e){}
16993 var days = date.getDaysInMonth();
16995 var firstOfMonth = date.getFirstDateOfMonth();
16996 var startingPos = firstOfMonth.getDay()-this.startDay;
16998 if(startingPos < this.startDay){
17002 var pm = date.add(Date.MONTH, -1);
17003 var prevStart = pm.getDaysInMonth()-startingPos;
17005 this.cells = this.el.select('.fc-day',true);
17006 this.textNodes = this.el.query('.fc-day-number');
17007 this.cells.addClassOnOver('fc-state-hover');
17009 var cells = this.cells.elements;
17010 var textEls = this.textNodes;
17012 Roo.each(cells, function(cell){
17013 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17016 days += startingPos;
17018 // convert everything to numbers so it's fast
17019 var day = 86400000;
17020 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17023 //Roo.log(prevStart);
17025 var today = new Date().clearTime().getTime();
17026 var sel = date.clearTime().getTime();
17027 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17028 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17029 var ddMatch = this.disabledDatesRE;
17030 var ddText = this.disabledDatesText;
17031 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17032 var ddaysText = this.disabledDaysText;
17033 var format = this.format;
17035 var setCellClass = function(cal, cell){
17039 //Roo.log('set Cell Class');
17041 var t = d.getTime();
17045 cell.dateValue = t;
17047 cell.className += " fc-today";
17048 cell.className += " fc-state-highlight";
17049 cell.title = cal.todayText;
17052 // disable highlight in other month..
17053 //cell.className += " fc-state-highlight";
17058 cell.className = " fc-state-disabled";
17059 cell.title = cal.minText;
17063 cell.className = " fc-state-disabled";
17064 cell.title = cal.maxText;
17068 if(ddays.indexOf(d.getDay()) != -1){
17069 cell.title = ddaysText;
17070 cell.className = " fc-state-disabled";
17073 if(ddMatch && format){
17074 var fvalue = d.dateFormat(format);
17075 if(ddMatch.test(fvalue)){
17076 cell.title = ddText.replace("%0", fvalue);
17077 cell.className = " fc-state-disabled";
17081 if (!cell.initialClassName) {
17082 cell.initialClassName = cell.dom.className;
17085 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17090 for(; i < startingPos; i++) {
17091 textEls[i].innerHTML = (++prevStart);
17092 d.setDate(d.getDate()+1);
17094 cells[i].className = "fc-past fc-other-month";
17095 setCellClass(this, cells[i]);
17100 for(; i < days; i++){
17101 intDay = i - startingPos + 1;
17102 textEls[i].innerHTML = (intDay);
17103 d.setDate(d.getDate()+1);
17105 cells[i].className = ''; // "x-date-active";
17106 setCellClass(this, cells[i]);
17110 for(; i < 42; i++) {
17111 textEls[i].innerHTML = (++extraDays);
17112 d.setDate(d.getDate()+1);
17114 cells[i].className = "fc-future fc-other-month";
17115 setCellClass(this, cells[i]);
17118 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17120 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17122 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17123 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17125 if(totalRows != 6){
17126 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17127 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17130 this.fireEvent('monthchange', this, date);
17134 if(!this.internalRender){
17135 var main = this.el.dom.firstChild;
17136 var w = main.offsetWidth;
17137 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17138 Roo.fly(main).setWidth(w);
17139 this.internalRender = true;
17140 // opera does not respect the auto grow header center column
17141 // then, after it gets a width opera refuses to recalculate
17142 // without a second pass
17143 if(Roo.isOpera && !this.secondPass){
17144 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17145 this.secondPass = true;
17146 this.update.defer(10, this, [date]);
17153 findCell : function(dt) {
17154 dt = dt.clearTime().getTime();
17156 this.cells.each(function(c){
17157 //Roo.log("check " +c.dateValue + '?=' + dt);
17158 if(c.dateValue == dt){
17168 findCells : function(ev) {
17169 var s = ev.start.clone().clearTime().getTime();
17171 var e= ev.end.clone().clearTime().getTime();
17174 this.cells.each(function(c){
17175 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17177 if(c.dateValue > e){
17180 if(c.dateValue < s){
17189 // findBestRow: function(cells)
17193 // for (var i =0 ; i < cells.length;i++) {
17194 // ret = Math.max(cells[i].rows || 0,ret);
17201 addItem : function(ev)
17203 // look for vertical location slot in
17204 var cells = this.findCells(ev);
17206 // ev.row = this.findBestRow(cells);
17208 // work out the location.
17212 for(var i =0; i < cells.length; i++) {
17214 cells[i].row = cells[0].row;
17217 cells[i].row = cells[i].row + 1;
17227 if (crow.start.getY() == cells[i].getY()) {
17229 crow.end = cells[i];
17246 cells[0].events.push(ev);
17248 this.calevents.push(ev);
17251 clearEvents: function() {
17253 if(!this.calevents){
17257 Roo.each(this.cells.elements, function(c){
17263 Roo.each(this.calevents, function(e) {
17264 Roo.each(e.els, function(el) {
17265 el.un('mouseenter' ,this.onEventEnter, this);
17266 el.un('mouseleave' ,this.onEventLeave, this);
17271 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17277 renderEvents: function()
17281 this.cells.each(function(c) {
17290 if(c.row != c.events.length){
17291 r = 4 - (4 - (c.row - c.events.length));
17294 c.events = ev.slice(0, r);
17295 c.more = ev.slice(r);
17297 if(c.more.length && c.more.length == 1){
17298 c.events.push(c.more.pop());
17301 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17305 this.cells.each(function(c) {
17307 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17310 for (var e = 0; e < c.events.length; e++){
17311 var ev = c.events[e];
17312 var rows = ev.rows;
17314 for(var i = 0; i < rows.length; i++) {
17316 // how many rows should it span..
17319 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17320 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17322 unselectable : "on",
17325 cls: 'fc-event-inner',
17329 // cls: 'fc-event-time',
17330 // html : cells.length > 1 ? '' : ev.time
17334 cls: 'fc-event-title',
17335 html : String.format('{0}', ev.title)
17342 cls: 'ui-resizable-handle ui-resizable-e',
17343 html : '  '
17350 cfg.cls += ' fc-event-start';
17352 if ((i+1) == rows.length) {
17353 cfg.cls += ' fc-event-end';
17356 var ctr = _this.el.select('.fc-event-container',true).first();
17357 var cg = ctr.createChild(cfg);
17359 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17360 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17362 var r = (c.more.length) ? 1 : 0;
17363 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17364 cg.setWidth(ebox.right - sbox.x -2);
17366 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17367 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17368 cg.on('click', _this.onEventClick, _this, ev);
17379 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17380 style : 'position: absolute',
17381 unselectable : "on",
17384 cls: 'fc-event-inner',
17388 cls: 'fc-event-title',
17396 cls: 'ui-resizable-handle ui-resizable-e',
17397 html : '  '
17403 var ctr = _this.el.select('.fc-event-container',true).first();
17404 var cg = ctr.createChild(cfg);
17406 var sbox = c.select('.fc-day-content',true).first().getBox();
17407 var ebox = c.select('.fc-day-content',true).first().getBox();
17409 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17410 cg.setWidth(ebox.right - sbox.x -2);
17412 cg.on('click', _this.onMoreEventClick, _this, c.more);
17422 onEventEnter: function (e, el,event,d) {
17423 this.fireEvent('evententer', this, el, event);
17426 onEventLeave: function (e, el,event,d) {
17427 this.fireEvent('eventleave', this, el, event);
17430 onEventClick: function (e, el,event,d) {
17431 this.fireEvent('eventclick', this, el, event);
17434 onMonthChange: function () {
17438 onMoreEventClick: function(e, el, more)
17442 this.calpopover.placement = 'right';
17443 this.calpopover.setTitle('More');
17445 this.calpopover.setContent('');
17447 var ctr = this.calpopover.el.select('.popover-content', true).first();
17449 Roo.each(more, function(m){
17451 cls : 'fc-event-hori fc-event-draggable',
17454 var cg = ctr.createChild(cfg);
17456 cg.on('click', _this.onEventClick, _this, m);
17459 this.calpopover.show(el);
17464 onLoad: function ()
17466 this.calevents = [];
17469 if(this.store.getCount() > 0){
17470 this.store.data.each(function(d){
17473 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17474 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17475 time : d.data.start_time,
17476 title : d.data.title,
17477 description : d.data.description,
17478 venue : d.data.venue
17483 this.renderEvents();
17485 if(this.calevents.length && this.loadMask){
17486 this.maskEl.hide();
17490 onBeforeLoad: function()
17492 this.clearEvents();
17494 this.maskEl.show();
17508 * @class Roo.bootstrap.Popover
17509 * @extends Roo.bootstrap.Component
17510 * Bootstrap Popover class
17511 * @cfg {String} html contents of the popover (or false to use children..)
17512 * @cfg {String} title of popover (or false to hide)
17513 * @cfg {String} placement how it is placed
17514 * @cfg {String} trigger click || hover (or false to trigger manually)
17515 * @cfg {String} over what (parent or false to trigger manually.)
17516 * @cfg {Number} delay - delay before showing
17519 * Create a new Popover
17520 * @param {Object} config The config object
17523 Roo.bootstrap.Popover = function(config){
17524 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17530 * After the popover show
17532 * @param {Roo.bootstrap.Popover} this
17537 * After the popover hide
17539 * @param {Roo.bootstrap.Popover} this
17545 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17547 title: 'Fill in a title',
17550 placement : 'right',
17551 trigger : 'hover', // hover
17557 can_build_overlaid : false,
17559 getChildContainer : function()
17561 return this.el.select('.popover-content',true).first();
17564 getAutoCreate : function(){
17567 cls : 'popover roo-dynamic',
17568 style: 'display:block',
17574 cls : 'popover-inner',
17578 cls: 'popover-title',
17582 cls : 'popover-content',
17593 setTitle: function(str)
17596 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17598 setContent: function(str)
17601 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17603 // as it get's added to the bottom of the page.
17604 onRender : function(ct, position)
17606 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17608 var cfg = Roo.apply({}, this.getAutoCreate());
17612 cfg.cls += ' ' + this.cls;
17615 cfg.style = this.style;
17617 //Roo.log("adding to ");
17618 this.el = Roo.get(document.body).createChild(cfg, position);
17619 // Roo.log(this.el);
17624 initEvents : function()
17626 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17627 this.el.enableDisplayMode('block');
17629 if (this.over === false) {
17632 if (this.triggers === false) {
17635 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17636 var triggers = this.trigger ? this.trigger.split(' ') : [];
17637 Roo.each(triggers, function(trigger) {
17639 if (trigger == 'click') {
17640 on_el.on('click', this.toggle, this);
17641 } else if (trigger != 'manual') {
17642 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17643 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17645 on_el.on(eventIn ,this.enter, this);
17646 on_el.on(eventOut, this.leave, this);
17657 toggle : function () {
17658 this.hoverState == 'in' ? this.leave() : this.enter();
17661 enter : function () {
17663 clearTimeout(this.timeout);
17665 this.hoverState = 'in';
17667 if (!this.delay || !this.delay.show) {
17672 this.timeout = setTimeout(function () {
17673 if (_t.hoverState == 'in') {
17676 }, this.delay.show)
17679 leave : function() {
17680 clearTimeout(this.timeout);
17682 this.hoverState = 'out';
17684 if (!this.delay || !this.delay.hide) {
17689 this.timeout = setTimeout(function () {
17690 if (_t.hoverState == 'out') {
17693 }, this.delay.hide)
17696 show : function (on_el)
17699 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17703 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17704 if (this.html !== false) {
17705 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17707 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17708 if (!this.title.length) {
17709 this.el.select('.popover-title',true).hide();
17712 var placement = typeof this.placement == 'function' ?
17713 this.placement.call(this, this.el, on_el) :
17716 var autoToken = /\s?auto?\s?/i;
17717 var autoPlace = autoToken.test(placement);
17719 placement = placement.replace(autoToken, '') || 'top';
17723 //this.el.setXY([0,0]);
17725 this.el.dom.style.display='block';
17726 this.el.addClass(placement);
17728 //this.el.appendTo(on_el);
17730 var p = this.getPosition();
17731 var box = this.el.getBox();
17736 var align = Roo.bootstrap.Popover.alignment[placement];
17739 this.el.alignTo(on_el, align[0],align[1]);
17740 //var arrow = this.el.select('.arrow',true).first();
17741 //arrow.set(align[2],
17743 this.el.addClass('in');
17746 if (this.el.hasClass('fade')) {
17750 this.hoverState = 'in';
17752 this.fireEvent('show', this);
17757 this.el.setXY([0,0]);
17758 this.el.removeClass('in');
17760 this.hoverState = null;
17762 this.fireEvent('hide', this);
17767 Roo.bootstrap.Popover.alignment = {
17768 'left' : ['r-l', [-10,0], 'right'],
17769 'right' : ['l-r', [10,0], 'left'],
17770 'bottom' : ['t-b', [0,10], 'top'],
17771 'top' : [ 'b-t', [0,-10], 'bottom']
17782 * @class Roo.bootstrap.Progress
17783 * @extends Roo.bootstrap.Component
17784 * Bootstrap Progress class
17785 * @cfg {Boolean} striped striped of the progress bar
17786 * @cfg {Boolean} active animated of the progress bar
17790 * Create a new Progress
17791 * @param {Object} config The config object
17794 Roo.bootstrap.Progress = function(config){
17795 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17798 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17803 getAutoCreate : function(){
17811 cfg.cls += ' progress-striped';
17815 cfg.cls += ' active';
17834 * @class Roo.bootstrap.ProgressBar
17835 * @extends Roo.bootstrap.Component
17836 * Bootstrap ProgressBar class
17837 * @cfg {Number} aria_valuenow aria-value now
17838 * @cfg {Number} aria_valuemin aria-value min
17839 * @cfg {Number} aria_valuemax aria-value max
17840 * @cfg {String} label label for the progress bar
17841 * @cfg {String} panel (success | info | warning | danger )
17842 * @cfg {String} role role of the progress bar
17843 * @cfg {String} sr_only text
17847 * Create a new ProgressBar
17848 * @param {Object} config The config object
17851 Roo.bootstrap.ProgressBar = function(config){
17852 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17855 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17859 aria_valuemax : 100,
17865 getAutoCreate : function()
17870 cls: 'progress-bar',
17871 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17883 cfg.role = this.role;
17886 if(this.aria_valuenow){
17887 cfg['aria-valuenow'] = this.aria_valuenow;
17890 if(this.aria_valuemin){
17891 cfg['aria-valuemin'] = this.aria_valuemin;
17894 if(this.aria_valuemax){
17895 cfg['aria-valuemax'] = this.aria_valuemax;
17898 if(this.label && !this.sr_only){
17899 cfg.html = this.label;
17903 cfg.cls += ' progress-bar-' + this.panel;
17909 update : function(aria_valuenow)
17911 this.aria_valuenow = aria_valuenow;
17913 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17928 * @class Roo.bootstrap.TabGroup
17929 * @extends Roo.bootstrap.Column
17930 * Bootstrap Column class
17931 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17932 * @cfg {Boolean} carousel true to make the group behave like a carousel
17933 * @cfg {Boolean} bullets show bullets for the panels
17934 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17935 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17936 * @cfg {Boolean} showarrow (true|false) show arrow default true
17939 * Create a new TabGroup
17940 * @param {Object} config The config object
17943 Roo.bootstrap.TabGroup = function(config){
17944 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17946 this.navId = Roo.id();
17949 Roo.bootstrap.TabGroup.register(this);
17953 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17956 transition : false,
17961 slideOnTouch : false,
17964 getAutoCreate : function()
17966 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17968 cfg.cls += ' tab-content';
17970 if (this.carousel) {
17971 cfg.cls += ' carousel slide';
17974 cls : 'carousel-inner',
17978 if(this.bullets && !Roo.isTouch){
17981 cls : 'carousel-bullets',
17985 if(this.bullets_cls){
17986 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17993 cfg.cn[0].cn.push(bullets);
17996 if(this.showarrow){
17997 cfg.cn[0].cn.push({
17999 class : 'carousel-arrow',
18003 class : 'carousel-prev',
18007 class : 'fa fa-chevron-left'
18013 class : 'carousel-next',
18017 class : 'fa fa-chevron-right'
18030 initEvents: function()
18032 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18033 // this.el.on("touchstart", this.onTouchStart, this);
18036 if(this.autoslide){
18039 this.slideFn = window.setInterval(function() {
18040 _this.showPanelNext();
18044 if(this.showarrow){
18045 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18046 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18052 // onTouchStart : function(e, el, o)
18054 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18058 // this.showPanelNext();
18062 getChildContainer : function()
18064 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18068 * register a Navigation item
18069 * @param {Roo.bootstrap.NavItem} the navitem to add
18071 register : function(item)
18073 this.tabs.push( item);
18074 item.navId = this.navId; // not really needed..
18079 getActivePanel : function()
18082 Roo.each(this.tabs, function(t) {
18092 getPanelByName : function(n)
18095 Roo.each(this.tabs, function(t) {
18096 if (t.tabId == n) {
18104 indexOfPanel : function(p)
18107 Roo.each(this.tabs, function(t,i) {
18108 if (t.tabId == p.tabId) {
18117 * show a specific panel
18118 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18119 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18121 showPanel : function (pan)
18123 if(this.transition || typeof(pan) == 'undefined'){
18124 Roo.log("waiting for the transitionend");
18128 if (typeof(pan) == 'number') {
18129 pan = this.tabs[pan];
18132 if (typeof(pan) == 'string') {
18133 pan = this.getPanelByName(pan);
18136 var cur = this.getActivePanel();
18139 Roo.log('pan or acitve pan is undefined');
18143 if (pan.tabId == this.getActivePanel().tabId) {
18147 if (false === cur.fireEvent('beforedeactivate')) {
18151 if(this.bullets > 0 && !Roo.isTouch){
18152 this.setActiveBullet(this.indexOfPanel(pan));
18155 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18157 this.transition = true;
18158 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18159 var lr = dir == 'next' ? 'left' : 'right';
18160 pan.el.addClass(dir); // or prev
18161 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18162 cur.el.addClass(lr); // or right
18163 pan.el.addClass(lr);
18166 cur.el.on('transitionend', function() {
18167 Roo.log("trans end?");
18169 pan.el.removeClass([lr,dir]);
18170 pan.setActive(true);
18172 cur.el.removeClass([lr]);
18173 cur.setActive(false);
18175 _this.transition = false;
18177 }, this, { single: true } );
18182 cur.setActive(false);
18183 pan.setActive(true);
18188 showPanelNext : function()
18190 var i = this.indexOfPanel(this.getActivePanel());
18192 if (i >= this.tabs.length - 1 && !this.autoslide) {
18196 if (i >= this.tabs.length - 1 && this.autoslide) {
18200 this.showPanel(this.tabs[i+1]);
18203 showPanelPrev : function()
18205 var i = this.indexOfPanel(this.getActivePanel());
18207 if (i < 1 && !this.autoslide) {
18211 if (i < 1 && this.autoslide) {
18212 i = this.tabs.length;
18215 this.showPanel(this.tabs[i-1]);
18219 addBullet: function()
18221 if(!this.bullets || Roo.isTouch){
18224 var ctr = this.el.select('.carousel-bullets',true).first();
18225 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18226 var bullet = ctr.createChild({
18227 cls : 'bullet bullet-' + i
18228 },ctr.dom.lastChild);
18233 bullet.on('click', (function(e, el, o, ii, t){
18235 e.preventDefault();
18237 this.showPanel(ii);
18239 if(this.autoslide && this.slideFn){
18240 clearInterval(this.slideFn);
18241 this.slideFn = window.setInterval(function() {
18242 _this.showPanelNext();
18246 }).createDelegate(this, [i, bullet], true));
18251 setActiveBullet : function(i)
18257 Roo.each(this.el.select('.bullet', true).elements, function(el){
18258 el.removeClass('selected');
18261 var bullet = this.el.select('.bullet-' + i, true).first();
18267 bullet.addClass('selected');
18278 Roo.apply(Roo.bootstrap.TabGroup, {
18282 * register a Navigation Group
18283 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18285 register : function(navgrp)
18287 this.groups[navgrp.navId] = navgrp;
18291 * fetch a Navigation Group based on the navigation ID
18292 * if one does not exist , it will get created.
18293 * @param {string} the navgroup to add
18294 * @returns {Roo.bootstrap.NavGroup} the navgroup
18296 get: function(navId) {
18297 if (typeof(this.groups[navId]) == 'undefined') {
18298 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18300 return this.groups[navId] ;
18315 * @class Roo.bootstrap.TabPanel
18316 * @extends Roo.bootstrap.Component
18317 * Bootstrap TabPanel class
18318 * @cfg {Boolean} active panel active
18319 * @cfg {String} html panel content
18320 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18321 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18322 * @cfg {String} href click to link..
18326 * Create a new TabPanel
18327 * @param {Object} config The config object
18330 Roo.bootstrap.TabPanel = function(config){
18331 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18335 * Fires when the active status changes
18336 * @param {Roo.bootstrap.TabPanel} this
18337 * @param {Boolean} state the new state
18342 * @event beforedeactivate
18343 * Fires before a tab is de-activated - can be used to do validation on a form.
18344 * @param {Roo.bootstrap.TabPanel} this
18345 * @return {Boolean} false if there is an error
18348 'beforedeactivate': true
18351 this.tabId = this.tabId || Roo.id();
18355 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18363 getAutoCreate : function(){
18366 // item is needed for carousel - not sure if it has any effect otherwise
18367 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18368 html: this.html || ''
18372 cfg.cls += ' active';
18376 cfg.tabId = this.tabId;
18383 initEvents: function()
18385 var p = this.parent();
18387 this.navId = this.navId || p.navId;
18389 if (typeof(this.navId) != 'undefined') {
18390 // not really needed.. but just in case.. parent should be a NavGroup.
18391 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18395 var i = tg.tabs.length - 1;
18397 if(this.active && tg.bullets > 0 && i < tg.bullets){
18398 tg.setActiveBullet(i);
18402 this.el.on('click', this.onClick, this);
18405 this.el.on("touchstart", this.onTouchStart, this);
18406 this.el.on("touchmove", this.onTouchMove, this);
18407 this.el.on("touchend", this.onTouchEnd, this);
18412 onRender : function(ct, position)
18414 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18417 setActive : function(state)
18419 Roo.log("panel - set active " + this.tabId + "=" + state);
18421 this.active = state;
18423 this.el.removeClass('active');
18425 } else if (!this.el.hasClass('active')) {
18426 this.el.addClass('active');
18429 this.fireEvent('changed', this, state);
18432 onClick : function(e)
18434 e.preventDefault();
18436 if(!this.href.length){
18440 window.location.href = this.href;
18449 onTouchStart : function(e)
18451 this.swiping = false;
18453 this.startX = e.browserEvent.touches[0].clientX;
18454 this.startY = e.browserEvent.touches[0].clientY;
18457 onTouchMove : function(e)
18459 this.swiping = true;
18461 this.endX = e.browserEvent.touches[0].clientX;
18462 this.endY = e.browserEvent.touches[0].clientY;
18465 onTouchEnd : function(e)
18472 var tabGroup = this.parent();
18474 if(this.endX > this.startX){ // swiping right
18475 tabGroup.showPanelPrev();
18479 if(this.startX > this.endX){ // swiping left
18480 tabGroup.showPanelNext();
18499 * @class Roo.bootstrap.DateField
18500 * @extends Roo.bootstrap.Input
18501 * Bootstrap DateField class
18502 * @cfg {Number} weekStart default 0
18503 * @cfg {String} viewMode default empty, (months|years)
18504 * @cfg {String} minViewMode default empty, (months|years)
18505 * @cfg {Number} startDate default -Infinity
18506 * @cfg {Number} endDate default Infinity
18507 * @cfg {Boolean} todayHighlight default false
18508 * @cfg {Boolean} todayBtn default false
18509 * @cfg {Boolean} calendarWeeks default false
18510 * @cfg {Object} daysOfWeekDisabled default empty
18511 * @cfg {Boolean} singleMode default false (true | false)
18513 * @cfg {Boolean} keyboardNavigation default true
18514 * @cfg {String} language default en
18517 * Create a new DateField
18518 * @param {Object} config The config object
18521 Roo.bootstrap.DateField = function(config){
18522 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18526 * Fires when this field show.
18527 * @param {Roo.bootstrap.DateField} this
18528 * @param {Mixed} date The date value
18533 * Fires when this field hide.
18534 * @param {Roo.bootstrap.DateField} this
18535 * @param {Mixed} date The date value
18540 * Fires when select a date.
18541 * @param {Roo.bootstrap.DateField} this
18542 * @param {Mixed} date The date value
18546 * @event beforeselect
18547 * Fires when before select a date.
18548 * @param {Roo.bootstrap.DateField} this
18549 * @param {Mixed} date The date value
18551 beforeselect : true
18555 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18558 * @cfg {String} format
18559 * The default date format string which can be overriden for localization support. The format must be
18560 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18564 * @cfg {String} altFormats
18565 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18566 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18568 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18576 todayHighlight : false,
18582 keyboardNavigation: true,
18584 calendarWeeks: false,
18586 startDate: -Infinity,
18590 daysOfWeekDisabled: [],
18594 singleMode : false,
18596 UTCDate: function()
18598 return new Date(Date.UTC.apply(Date, arguments));
18601 UTCToday: function()
18603 var today = new Date();
18604 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18607 getDate: function() {
18608 var d = this.getUTCDate();
18609 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18612 getUTCDate: function() {
18616 setDate: function(d) {
18617 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18620 setUTCDate: function(d) {
18622 this.setValue(this.formatDate(this.date));
18625 onRender: function(ct, position)
18628 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18630 this.language = this.language || 'en';
18631 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18632 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18634 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18635 this.format = this.format || 'm/d/y';
18636 this.isInline = false;
18637 this.isInput = true;
18638 this.component = this.el.select('.add-on', true).first() || false;
18639 this.component = (this.component && this.component.length === 0) ? false : this.component;
18640 this.hasInput = this.component && this.inputEl().length;
18642 if (typeof(this.minViewMode === 'string')) {
18643 switch (this.minViewMode) {
18645 this.minViewMode = 1;
18648 this.minViewMode = 2;
18651 this.minViewMode = 0;
18656 if (typeof(this.viewMode === 'string')) {
18657 switch (this.viewMode) {
18670 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18672 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18674 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18676 this.picker().on('mousedown', this.onMousedown, this);
18677 this.picker().on('click', this.onClick, this);
18679 this.picker().addClass('datepicker-dropdown');
18681 this.startViewMode = this.viewMode;
18683 if(this.singleMode){
18684 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18685 v.setVisibilityMode(Roo.Element.DISPLAY);
18689 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18690 v.setStyle('width', '189px');
18694 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18695 if(!this.calendarWeeks){
18700 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18701 v.attr('colspan', function(i, val){
18702 return parseInt(val) + 1;
18707 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18709 this.setStartDate(this.startDate);
18710 this.setEndDate(this.endDate);
18712 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18719 if(this.isInline) {
18724 picker : function()
18726 return this.pickerEl;
18727 // return this.el.select('.datepicker', true).first();
18730 fillDow: function()
18732 var dowCnt = this.weekStart;
18741 if(this.calendarWeeks){
18749 while (dowCnt < this.weekStart + 7) {
18753 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18757 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18760 fillMonths: function()
18763 var months = this.picker().select('>.datepicker-months td', true).first();
18765 months.dom.innerHTML = '';
18771 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18774 months.createChild(month);
18781 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;
18783 if (this.date < this.startDate) {
18784 this.viewDate = new Date(this.startDate);
18785 } else if (this.date > this.endDate) {
18786 this.viewDate = new Date(this.endDate);
18788 this.viewDate = new Date(this.date);
18796 var d = new Date(this.viewDate),
18797 year = d.getUTCFullYear(),
18798 month = d.getUTCMonth(),
18799 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18800 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18801 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18802 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18803 currentDate = this.date && this.date.valueOf(),
18804 today = this.UTCToday();
18806 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18808 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18810 // this.picker.select('>tfoot th.today').
18811 // .text(dates[this.language].today)
18812 // .toggle(this.todayBtn !== false);
18814 this.updateNavArrows();
18817 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18819 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18821 prevMonth.setUTCDate(day);
18823 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18825 var nextMonth = new Date(prevMonth);
18827 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18829 nextMonth = nextMonth.valueOf();
18831 var fillMonths = false;
18833 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18835 while(prevMonth.valueOf() <= nextMonth) {
18838 if (prevMonth.getUTCDay() === this.weekStart) {
18840 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18848 if(this.calendarWeeks){
18849 // ISO 8601: First week contains first thursday.
18850 // ISO also states week starts on Monday, but we can be more abstract here.
18852 // Start of current week: based on weekstart/current date
18853 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18854 // Thursday of this week
18855 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18856 // First Thursday of year, year from thursday
18857 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18858 // Calendar week: ms between thursdays, div ms per day, div 7 days
18859 calWeek = (th - yth) / 864e5 / 7 + 1;
18861 fillMonths.cn.push({
18869 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18871 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18874 if (this.todayHighlight &&
18875 prevMonth.getUTCFullYear() == today.getFullYear() &&
18876 prevMonth.getUTCMonth() == today.getMonth() &&
18877 prevMonth.getUTCDate() == today.getDate()) {
18878 clsName += ' today';
18881 if (currentDate && prevMonth.valueOf() === currentDate) {
18882 clsName += ' active';
18885 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18886 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18887 clsName += ' disabled';
18890 fillMonths.cn.push({
18892 cls: 'day ' + clsName,
18893 html: prevMonth.getDate()
18896 prevMonth.setDate(prevMonth.getDate()+1);
18899 var currentYear = this.date && this.date.getUTCFullYear();
18900 var currentMonth = this.date && this.date.getUTCMonth();
18902 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18904 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18905 v.removeClass('active');
18907 if(currentYear === year && k === currentMonth){
18908 v.addClass('active');
18911 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18912 v.addClass('disabled');
18918 year = parseInt(year/10, 10) * 10;
18920 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18922 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18925 for (var i = -1; i < 11; i++) {
18926 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18928 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18936 showMode: function(dir)
18939 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18942 Roo.each(this.picker().select('>div',true).elements, function(v){
18943 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18946 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18951 if(this.isInline) {
18955 this.picker().removeClass(['bottom', 'top']);
18957 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18959 * place to the top of element!
18963 this.picker().addClass('top');
18964 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18969 this.picker().addClass('bottom');
18971 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18974 parseDate : function(value)
18976 if(!value || value instanceof Date){
18979 var v = Date.parseDate(value, this.format);
18980 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18981 v = Date.parseDate(value, 'Y-m-d');
18983 if(!v && this.altFormats){
18984 if(!this.altFormatsArray){
18985 this.altFormatsArray = this.altFormats.split("|");
18987 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18988 v = Date.parseDate(value, this.altFormatsArray[i]);
18994 formatDate : function(date, fmt)
18996 return (!date || !(date instanceof Date)) ?
18997 date : date.dateFormat(fmt || this.format);
19000 onFocus : function()
19002 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19006 onBlur : function()
19008 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19010 var d = this.inputEl().getValue();
19017 showPopup : function()
19019 this.picker().show();
19023 this.fireEvent('showpopup', this, this.date);
19026 hidePopup : function()
19028 if(this.isInline) {
19031 this.picker().hide();
19032 this.viewMode = this.startViewMode;
19035 this.fireEvent('hidepopup', this, this.date);
19039 onMousedown: function(e)
19041 e.stopPropagation();
19042 e.preventDefault();
19047 Roo.bootstrap.DateField.superclass.keyup.call(this);
19051 setValue: function(v)
19053 if(this.fireEvent('beforeselect', this, v) !== false){
19054 var d = new Date(this.parseDate(v) ).clearTime();
19056 if(isNaN(d.getTime())){
19057 this.date = this.viewDate = '';
19058 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19062 v = this.formatDate(d);
19064 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19066 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19070 this.fireEvent('select', this, this.date);
19074 getValue: function()
19076 return this.formatDate(this.date);
19079 fireKey: function(e)
19081 if (!this.picker().isVisible()){
19082 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19088 var dateChanged = false,
19090 newDate, newViewDate;
19095 e.preventDefault();
19099 if (!this.keyboardNavigation) {
19102 dir = e.keyCode == 37 ? -1 : 1;
19105 newDate = this.moveYear(this.date, dir);
19106 newViewDate = this.moveYear(this.viewDate, dir);
19107 } else if (e.shiftKey){
19108 newDate = this.moveMonth(this.date, dir);
19109 newViewDate = this.moveMonth(this.viewDate, dir);
19111 newDate = new Date(this.date);
19112 newDate.setUTCDate(this.date.getUTCDate() + dir);
19113 newViewDate = new Date(this.viewDate);
19114 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19116 if (this.dateWithinRange(newDate)){
19117 this.date = newDate;
19118 this.viewDate = newViewDate;
19119 this.setValue(this.formatDate(this.date));
19121 e.preventDefault();
19122 dateChanged = true;
19127 if (!this.keyboardNavigation) {
19130 dir = e.keyCode == 38 ? -1 : 1;
19132 newDate = this.moveYear(this.date, dir);
19133 newViewDate = this.moveYear(this.viewDate, dir);
19134 } else if (e.shiftKey){
19135 newDate = this.moveMonth(this.date, dir);
19136 newViewDate = this.moveMonth(this.viewDate, dir);
19138 newDate = new Date(this.date);
19139 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19140 newViewDate = new Date(this.viewDate);
19141 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19143 if (this.dateWithinRange(newDate)){
19144 this.date = newDate;
19145 this.viewDate = newViewDate;
19146 this.setValue(this.formatDate(this.date));
19148 e.preventDefault();
19149 dateChanged = true;
19153 this.setValue(this.formatDate(this.date));
19155 e.preventDefault();
19158 this.setValue(this.formatDate(this.date));
19172 onClick: function(e)
19174 e.stopPropagation();
19175 e.preventDefault();
19177 var target = e.getTarget();
19179 if(target.nodeName.toLowerCase() === 'i'){
19180 target = Roo.get(target).dom.parentNode;
19183 var nodeName = target.nodeName;
19184 var className = target.className;
19185 var html = target.innerHTML;
19186 //Roo.log(nodeName);
19188 switch(nodeName.toLowerCase()) {
19190 switch(className) {
19196 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19197 switch(this.viewMode){
19199 this.viewDate = this.moveMonth(this.viewDate, dir);
19203 this.viewDate = this.moveYear(this.viewDate, dir);
19209 var date = new Date();
19210 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19212 this.setValue(this.formatDate(this.date));
19219 if (className.indexOf('disabled') < 0) {
19220 this.viewDate.setUTCDate(1);
19221 if (className.indexOf('month') > -1) {
19222 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19224 var year = parseInt(html, 10) || 0;
19225 this.viewDate.setUTCFullYear(year);
19229 if(this.singleMode){
19230 this.setValue(this.formatDate(this.viewDate));
19241 //Roo.log(className);
19242 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19243 var day = parseInt(html, 10) || 1;
19244 var year = this.viewDate.getUTCFullYear(),
19245 month = this.viewDate.getUTCMonth();
19247 if (className.indexOf('old') > -1) {
19254 } else if (className.indexOf('new') > -1) {
19262 //Roo.log([year,month,day]);
19263 this.date = this.UTCDate(year, month, day,0,0,0,0);
19264 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19266 //Roo.log(this.formatDate(this.date));
19267 this.setValue(this.formatDate(this.date));
19274 setStartDate: function(startDate)
19276 this.startDate = startDate || -Infinity;
19277 if (this.startDate !== -Infinity) {
19278 this.startDate = this.parseDate(this.startDate);
19281 this.updateNavArrows();
19284 setEndDate: function(endDate)
19286 this.endDate = endDate || Infinity;
19287 if (this.endDate !== Infinity) {
19288 this.endDate = this.parseDate(this.endDate);
19291 this.updateNavArrows();
19294 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19296 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19297 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19298 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19300 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19301 return parseInt(d, 10);
19304 this.updateNavArrows();
19307 updateNavArrows: function()
19309 if(this.singleMode){
19313 var d = new Date(this.viewDate),
19314 year = d.getUTCFullYear(),
19315 month = d.getUTCMonth();
19317 Roo.each(this.picker().select('.prev', true).elements, function(v){
19319 switch (this.viewMode) {
19322 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19328 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19335 Roo.each(this.picker().select('.next', true).elements, function(v){
19337 switch (this.viewMode) {
19340 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19346 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19354 moveMonth: function(date, dir)
19359 var new_date = new Date(date.valueOf()),
19360 day = new_date.getUTCDate(),
19361 month = new_date.getUTCMonth(),
19362 mag = Math.abs(dir),
19364 dir = dir > 0 ? 1 : -1;
19367 // If going back one month, make sure month is not current month
19368 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19370 return new_date.getUTCMonth() == month;
19372 // If going forward one month, make sure month is as expected
19373 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19375 return new_date.getUTCMonth() != new_month;
19377 new_month = month + dir;
19378 new_date.setUTCMonth(new_month);
19379 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19380 if (new_month < 0 || new_month > 11) {
19381 new_month = (new_month + 12) % 12;
19384 // For magnitudes >1, move one month at a time...
19385 for (var i=0; i<mag; i++) {
19386 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19387 new_date = this.moveMonth(new_date, dir);
19389 // ...then reset the day, keeping it in the new month
19390 new_month = new_date.getUTCMonth();
19391 new_date.setUTCDate(day);
19393 return new_month != new_date.getUTCMonth();
19396 // Common date-resetting loop -- if date is beyond end of month, make it
19399 new_date.setUTCDate(--day);
19400 new_date.setUTCMonth(new_month);
19405 moveYear: function(date, dir)
19407 return this.moveMonth(date, dir*12);
19410 dateWithinRange: function(date)
19412 return date >= this.startDate && date <= this.endDate;
19418 this.picker().remove();
19421 validateValue : function(value)
19423 if(this.getVisibilityEl().hasClass('hidden')){
19427 if(value.length < 1) {
19428 if(this.allowBlank){
19434 if(value.length < this.minLength){
19437 if(value.length > this.maxLength){
19441 var vt = Roo.form.VTypes;
19442 if(!vt[this.vtype](value, this)){
19446 if(typeof this.validator == "function"){
19447 var msg = this.validator(value);
19453 if(this.regex && !this.regex.test(value)){
19457 if(typeof(this.parseDate(value)) == 'undefined'){
19461 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19465 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19475 this.date = this.viewDate = '';
19477 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19482 Roo.apply(Roo.bootstrap.DateField, {
19493 html: '<i class="fa fa-arrow-left"/>'
19503 html: '<i class="fa fa-arrow-right"/>'
19545 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19546 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19547 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19548 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19549 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19562 navFnc: 'FullYear',
19567 navFnc: 'FullYear',
19572 Roo.apply(Roo.bootstrap.DateField, {
19576 cls: 'datepicker dropdown-menu roo-dynamic',
19580 cls: 'datepicker-days',
19584 cls: 'table-condensed',
19586 Roo.bootstrap.DateField.head,
19590 Roo.bootstrap.DateField.footer
19597 cls: 'datepicker-months',
19601 cls: 'table-condensed',
19603 Roo.bootstrap.DateField.head,
19604 Roo.bootstrap.DateField.content,
19605 Roo.bootstrap.DateField.footer
19612 cls: 'datepicker-years',
19616 cls: 'table-condensed',
19618 Roo.bootstrap.DateField.head,
19619 Roo.bootstrap.DateField.content,
19620 Roo.bootstrap.DateField.footer
19639 * @class Roo.bootstrap.TimeField
19640 * @extends Roo.bootstrap.Input
19641 * Bootstrap DateField class
19645 * Create a new TimeField
19646 * @param {Object} config The config object
19649 Roo.bootstrap.TimeField = function(config){
19650 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19654 * Fires when this field show.
19655 * @param {Roo.bootstrap.DateField} thisthis
19656 * @param {Mixed} date The date value
19661 * Fires when this field hide.
19662 * @param {Roo.bootstrap.DateField} this
19663 * @param {Mixed} date The date value
19668 * Fires when select a date.
19669 * @param {Roo.bootstrap.DateField} this
19670 * @param {Mixed} date The date value
19676 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19679 * @cfg {String} format
19680 * The default time format string which can be overriden for localization support. The format must be
19681 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19685 onRender: function(ct, position)
19688 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19690 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19692 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19694 this.pop = this.picker().select('>.datepicker-time',true).first();
19695 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19697 this.picker().on('mousedown', this.onMousedown, this);
19698 this.picker().on('click', this.onClick, this);
19700 this.picker().addClass('datepicker-dropdown');
19705 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19706 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19707 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19708 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19709 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19710 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19714 fireKey: function(e){
19715 if (!this.picker().isVisible()){
19716 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19722 e.preventDefault();
19730 this.onTogglePeriod();
19733 this.onIncrementMinutes();
19736 this.onDecrementMinutes();
19745 onClick: function(e) {
19746 e.stopPropagation();
19747 e.preventDefault();
19750 picker : function()
19752 return this.el.select('.datepicker', true).first();
19755 fillTime: function()
19757 var time = this.pop.select('tbody', true).first();
19759 time.dom.innerHTML = '';
19774 cls: 'hours-up glyphicon glyphicon-chevron-up'
19794 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19815 cls: 'timepicker-hour',
19830 cls: 'timepicker-minute',
19845 cls: 'btn btn-primary period',
19867 cls: 'hours-down glyphicon glyphicon-chevron-down'
19887 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19905 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19912 var hours = this.time.getHours();
19913 var minutes = this.time.getMinutes();
19926 hours = hours - 12;
19930 hours = '0' + hours;
19934 minutes = '0' + minutes;
19937 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19938 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19939 this.pop.select('button', true).first().dom.innerHTML = period;
19945 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19947 var cls = ['bottom'];
19949 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19956 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19961 this.picker().addClass(cls.join('-'));
19965 Roo.each(cls, function(c){
19967 _this.picker().setTop(_this.inputEl().getHeight());
19971 _this.picker().setTop(0 - _this.picker().getHeight());
19976 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19980 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19987 onFocus : function()
19989 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19993 onBlur : function()
19995 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20001 this.picker().show();
20006 this.fireEvent('show', this, this.date);
20011 this.picker().hide();
20014 this.fireEvent('hide', this, this.date);
20017 setTime : function()
20020 this.setValue(this.time.format(this.format));
20022 this.fireEvent('select', this, this.date);
20027 onMousedown: function(e){
20028 e.stopPropagation();
20029 e.preventDefault();
20032 onIncrementHours: function()
20034 Roo.log('onIncrementHours');
20035 this.time = this.time.add(Date.HOUR, 1);
20040 onDecrementHours: function()
20042 Roo.log('onDecrementHours');
20043 this.time = this.time.add(Date.HOUR, -1);
20047 onIncrementMinutes: function()
20049 Roo.log('onIncrementMinutes');
20050 this.time = this.time.add(Date.MINUTE, 1);
20054 onDecrementMinutes: function()
20056 Roo.log('onDecrementMinutes');
20057 this.time = this.time.add(Date.MINUTE, -1);
20061 onTogglePeriod: function()
20063 Roo.log('onTogglePeriod');
20064 this.time = this.time.add(Date.HOUR, 12);
20071 Roo.apply(Roo.bootstrap.TimeField, {
20101 cls: 'btn btn-info ok',
20113 Roo.apply(Roo.bootstrap.TimeField, {
20117 cls: 'datepicker dropdown-menu',
20121 cls: 'datepicker-time',
20125 cls: 'table-condensed',
20127 Roo.bootstrap.TimeField.content,
20128 Roo.bootstrap.TimeField.footer
20147 * @class Roo.bootstrap.MonthField
20148 * @extends Roo.bootstrap.Input
20149 * Bootstrap MonthField class
20151 * @cfg {String} language default en
20154 * Create a new MonthField
20155 * @param {Object} config The config object
20158 Roo.bootstrap.MonthField = function(config){
20159 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20164 * Fires when this field show.
20165 * @param {Roo.bootstrap.MonthField} this
20166 * @param {Mixed} date The date value
20171 * Fires when this field hide.
20172 * @param {Roo.bootstrap.MonthField} this
20173 * @param {Mixed} date The date value
20178 * Fires when select a date.
20179 * @param {Roo.bootstrap.MonthField} this
20180 * @param {String} oldvalue The old value
20181 * @param {String} newvalue The new value
20187 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20189 onRender: function(ct, position)
20192 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20194 this.language = this.language || 'en';
20195 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20196 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20198 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20199 this.isInline = false;
20200 this.isInput = true;
20201 this.component = this.el.select('.add-on', true).first() || false;
20202 this.component = (this.component && this.component.length === 0) ? false : this.component;
20203 this.hasInput = this.component && this.inputEL().length;
20205 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20207 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20209 this.picker().on('mousedown', this.onMousedown, this);
20210 this.picker().on('click', this.onClick, this);
20212 this.picker().addClass('datepicker-dropdown');
20214 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20215 v.setStyle('width', '189px');
20222 if(this.isInline) {
20228 setValue: function(v, suppressEvent)
20230 var o = this.getValue();
20232 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20236 if(suppressEvent !== true){
20237 this.fireEvent('select', this, o, v);
20242 getValue: function()
20247 onClick: function(e)
20249 e.stopPropagation();
20250 e.preventDefault();
20252 var target = e.getTarget();
20254 if(target.nodeName.toLowerCase() === 'i'){
20255 target = Roo.get(target).dom.parentNode;
20258 var nodeName = target.nodeName;
20259 var className = target.className;
20260 var html = target.innerHTML;
20262 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20266 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20268 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20274 picker : function()
20276 return this.pickerEl;
20279 fillMonths: function()
20282 var months = this.picker().select('>.datepicker-months td', true).first();
20284 months.dom.innerHTML = '';
20290 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20293 months.createChild(month);
20302 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20303 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20306 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20307 e.removeClass('active');
20309 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20310 e.addClass('active');
20317 if(this.isInline) {
20321 this.picker().removeClass(['bottom', 'top']);
20323 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20325 * place to the top of element!
20329 this.picker().addClass('top');
20330 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20335 this.picker().addClass('bottom');
20337 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20340 onFocus : function()
20342 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20346 onBlur : function()
20348 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20350 var d = this.inputEl().getValue();
20359 this.picker().show();
20360 this.picker().select('>.datepicker-months', true).first().show();
20364 this.fireEvent('show', this, this.date);
20369 if(this.isInline) {
20372 this.picker().hide();
20373 this.fireEvent('hide', this, this.date);
20377 onMousedown: function(e)
20379 e.stopPropagation();
20380 e.preventDefault();
20385 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20389 fireKey: function(e)
20391 if (!this.picker().isVisible()){
20392 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20403 e.preventDefault();
20407 dir = e.keyCode == 37 ? -1 : 1;
20409 this.vIndex = this.vIndex + dir;
20411 if(this.vIndex < 0){
20415 if(this.vIndex > 11){
20419 if(isNaN(this.vIndex)){
20423 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20429 dir = e.keyCode == 38 ? -1 : 1;
20431 this.vIndex = this.vIndex + dir * 4;
20433 if(this.vIndex < 0){
20437 if(this.vIndex > 11){
20441 if(isNaN(this.vIndex)){
20445 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20450 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20451 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20455 e.preventDefault();
20458 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20459 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20475 this.picker().remove();
20480 Roo.apply(Roo.bootstrap.MonthField, {
20499 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20500 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20505 Roo.apply(Roo.bootstrap.MonthField, {
20509 cls: 'datepicker dropdown-menu roo-dynamic',
20513 cls: 'datepicker-months',
20517 cls: 'table-condensed',
20519 Roo.bootstrap.DateField.content
20539 * @class Roo.bootstrap.CheckBox
20540 * @extends Roo.bootstrap.Input
20541 * Bootstrap CheckBox class
20543 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20544 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20545 * @cfg {String} boxLabel The text that appears beside the checkbox
20546 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20547 * @cfg {Boolean} checked initnal the element
20548 * @cfg {Boolean} inline inline the element (default false)
20549 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20550 * @cfg {String} tooltip label tooltip
20553 * Create a new CheckBox
20554 * @param {Object} config The config object
20557 Roo.bootstrap.CheckBox = function(config){
20558 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20563 * Fires when the element is checked or unchecked.
20564 * @param {Roo.bootstrap.CheckBox} this This input
20565 * @param {Boolean} checked The new checked value
20570 * Fires when the element is click.
20571 * @param {Roo.bootstrap.CheckBox} this This input
20578 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20580 inputType: 'checkbox',
20589 getAutoCreate : function()
20591 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20597 cfg.cls = 'form-group ' + this.inputType; //input-group
20600 cfg.cls += ' ' + this.inputType + '-inline';
20606 type : this.inputType,
20607 value : this.inputValue,
20608 cls : 'roo-' + this.inputType, //'form-box',
20609 placeholder : this.placeholder || ''
20613 if(this.inputType != 'radio'){
20617 cls : 'roo-hidden-value',
20618 value : this.checked ? this.inputValue : this.valueOff
20623 if (this.weight) { // Validity check?
20624 cfg.cls += " " + this.inputType + "-" + this.weight;
20627 if (this.disabled) {
20628 input.disabled=true;
20632 input.checked = this.checked;
20637 input.name = this.name;
20639 if(this.inputType != 'radio'){
20640 hidden.name = this.name;
20641 input.name = '_hidden_' + this.name;
20646 input.cls += ' input-' + this.size;
20651 ['xs','sm','md','lg'].map(function(size){
20652 if (settings[size]) {
20653 cfg.cls += ' col-' + size + '-' + settings[size];
20657 var inputblock = input;
20659 if (this.before || this.after) {
20662 cls : 'input-group',
20667 inputblock.cn.push({
20669 cls : 'input-group-addon',
20674 inputblock.cn.push(input);
20676 if(this.inputType != 'radio'){
20677 inputblock.cn.push(hidden);
20681 inputblock.cn.push({
20683 cls : 'input-group-addon',
20690 if (align ==='left' && this.fieldLabel.length) {
20691 // Roo.log("left and has label");
20696 cls : 'control-label',
20697 html : this.fieldLabel
20707 if(this.labelWidth > 12){
20708 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20711 if(this.labelWidth < 13 && this.labelmd == 0){
20712 this.labelmd = this.labelWidth;
20715 if(this.labellg > 0){
20716 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20717 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20720 if(this.labelmd > 0){
20721 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20722 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20725 if(this.labelsm > 0){
20726 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20727 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20730 if(this.labelxs > 0){
20731 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20732 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20735 } else if ( this.fieldLabel.length) {
20736 // Roo.log(" label");
20740 tag: this.boxLabel ? 'span' : 'label',
20742 cls: 'control-label box-input-label',
20743 //cls : 'input-group-addon',
20744 html : this.fieldLabel
20753 // Roo.log(" no label && no align");
20754 cfg.cn = [ inputblock ] ;
20760 var boxLabelCfg = {
20762 //'for': id, // box label is handled by onclick - so no for...
20764 html: this.boxLabel
20768 boxLabelCfg.tooltip = this.tooltip;
20771 cfg.cn.push(boxLabelCfg);
20774 if(this.inputType != 'radio'){
20775 cfg.cn.push(hidden);
20783 * return the real input element.
20785 inputEl: function ()
20787 return this.el.select('input.roo-' + this.inputType,true).first();
20789 hiddenEl: function ()
20791 return this.el.select('input.roo-hidden-value',true).first();
20794 labelEl: function()
20796 return this.el.select('label.control-label',true).first();
20798 /* depricated... */
20802 return this.labelEl();
20805 boxLabelEl: function()
20807 return this.el.select('label.box-label',true).first();
20810 initEvents : function()
20812 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20814 this.inputEl().on('click', this.onClick, this);
20816 if (this.boxLabel) {
20817 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20820 this.startValue = this.getValue();
20823 Roo.bootstrap.CheckBox.register(this);
20827 onClick : function(e)
20829 if(this.fireEvent('click', this, e) !== false){
20830 this.setChecked(!this.checked);
20835 setChecked : function(state,suppressEvent)
20837 this.startValue = this.getValue();
20839 if(this.inputType == 'radio'){
20841 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20842 e.dom.checked = false;
20845 this.inputEl().dom.checked = true;
20847 this.inputEl().dom.value = this.inputValue;
20849 if(suppressEvent !== true){
20850 this.fireEvent('check', this, true);
20858 this.checked = state;
20860 this.inputEl().dom.checked = state;
20863 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20865 if(suppressEvent !== true){
20866 this.fireEvent('check', this, state);
20872 getValue : function()
20874 if(this.inputType == 'radio'){
20875 return this.getGroupValue();
20878 return this.hiddenEl().dom.value;
20882 getGroupValue : function()
20884 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20888 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20891 setValue : function(v,suppressEvent)
20893 if(this.inputType == 'radio'){
20894 this.setGroupValue(v, suppressEvent);
20898 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20903 setGroupValue : function(v, suppressEvent)
20905 this.startValue = this.getValue();
20907 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20908 e.dom.checked = false;
20910 if(e.dom.value == v){
20911 e.dom.checked = true;
20915 if(suppressEvent !== true){
20916 this.fireEvent('check', this, true);
20924 validate : function()
20926 if(this.getVisibilityEl().hasClass('hidden')){
20932 (this.inputType == 'radio' && this.validateRadio()) ||
20933 (this.inputType == 'checkbox' && this.validateCheckbox())
20939 this.markInvalid();
20943 validateRadio : function()
20945 if(this.getVisibilityEl().hasClass('hidden')){
20949 if(this.allowBlank){
20955 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20956 if(!e.dom.checked){
20968 validateCheckbox : function()
20971 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20972 //return (this.getValue() == this.inputValue) ? true : false;
20975 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20983 for(var i in group){
20984 if(group[i].el.isVisible(true)){
20992 for(var i in group){
20997 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21004 * Mark this field as valid
21006 markValid : function()
21010 this.fireEvent('valid', this);
21012 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21015 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21022 if(this.inputType == 'radio'){
21023 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21024 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21025 e.findParent('.form-group', false, true).addClass(_this.validClass);
21032 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21033 this.el.findParent('.form-group', false, true).addClass(this.validClass);
21037 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21043 for(var i in group){
21044 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21045 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21050 * Mark this field as invalid
21051 * @param {String} msg The validation message
21053 markInvalid : function(msg)
21055 if(this.allowBlank){
21061 this.fireEvent('invalid', this, msg);
21063 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21066 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21070 label.markInvalid();
21073 if(this.inputType == 'radio'){
21074 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21075 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21076 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21083 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21084 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21088 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21094 for(var i in group){
21095 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21096 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21101 clearInvalid : function()
21103 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21105 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21107 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21109 if (label && label.iconEl) {
21110 label.iconEl.removeClass(label.validClass);
21111 label.iconEl.removeClass(label.invalidClass);
21115 disable : function()
21117 if(this.inputType != 'radio'){
21118 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21125 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21126 _this.getActionEl().addClass(this.disabledClass);
21127 e.dom.disabled = true;
21131 this.disabled = true;
21132 this.fireEvent("disable", this);
21136 enable : function()
21138 if(this.inputType != 'radio'){
21139 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21146 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21147 _this.getActionEl().removeClass(this.disabledClass);
21148 e.dom.disabled = false;
21152 this.disabled = false;
21153 this.fireEvent("enable", this);
21157 setBoxLabel : function(v)
21162 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21168 Roo.apply(Roo.bootstrap.CheckBox, {
21173 * register a CheckBox Group
21174 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21176 register : function(checkbox)
21178 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21179 this.groups[checkbox.groupId] = {};
21182 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21186 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21190 * fetch a CheckBox Group based on the group ID
21191 * @param {string} the group ID
21192 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21194 get: function(groupId) {
21195 if (typeof(this.groups[groupId]) == 'undefined') {
21199 return this.groups[groupId] ;
21212 * @class Roo.bootstrap.Radio
21213 * @extends Roo.bootstrap.Component
21214 * Bootstrap Radio class
21215 * @cfg {String} boxLabel - the label associated
21216 * @cfg {String} value - the value of radio
21219 * Create a new Radio
21220 * @param {Object} config The config object
21222 Roo.bootstrap.Radio = function(config){
21223 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21227 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21233 getAutoCreate : function()
21237 cls : 'form-group radio',
21242 html : this.boxLabel
21250 initEvents : function()
21252 this.parent().register(this);
21254 this.el.on('click', this.onClick, this);
21258 onClick : function(e)
21260 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21261 this.setChecked(true);
21265 setChecked : function(state, suppressEvent)
21267 this.parent().setValue(this.value, suppressEvent);
21271 setBoxLabel : function(v)
21276 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21291 * @class Roo.bootstrap.SecurePass
21292 * @extends Roo.bootstrap.Input
21293 * Bootstrap SecurePass class
21297 * Create a new SecurePass
21298 * @param {Object} config The config object
21301 Roo.bootstrap.SecurePass = function (config) {
21302 // these go here, so the translation tool can replace them..
21304 PwdEmpty: "Please type a password, and then retype it to confirm.",
21305 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21306 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21307 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21308 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21309 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21310 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21311 TooWeak: "Your password is Too Weak."
21313 this.meterLabel = "Password strength:";
21314 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21315 this.meterClass = [
21316 "roo-password-meter-tooweak",
21317 "roo-password-meter-weak",
21318 "roo-password-meter-medium",
21319 "roo-password-meter-strong",
21320 "roo-password-meter-grey"
21325 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21328 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21330 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21332 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21333 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21334 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21335 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21336 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21337 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21338 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21348 * @cfg {String/Object} Label for the strength meter (defaults to
21349 * 'Password strength:')
21354 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21355 * ['Weak', 'Medium', 'Strong'])
21358 pwdStrengths: false,
21371 initEvents: function ()
21373 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21375 if (this.el.is('input[type=password]') && Roo.isSafari) {
21376 this.el.on('keydown', this.SafariOnKeyDown, this);
21379 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21382 onRender: function (ct, position)
21384 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21385 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21386 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21388 this.trigger.createChild({
21393 cls: 'roo-password-meter-grey col-xs-12',
21396 //width: this.meterWidth + 'px'
21400 cls: 'roo-password-meter-text'
21406 if (this.hideTrigger) {
21407 this.trigger.setDisplayed(false);
21409 this.setSize(this.width || '', this.height || '');
21412 onDestroy: function ()
21414 if (this.trigger) {
21415 this.trigger.removeAllListeners();
21416 this.trigger.remove();
21419 this.wrap.remove();
21421 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21424 checkStrength: function ()
21426 var pwd = this.inputEl().getValue();
21427 if (pwd == this._lastPwd) {
21432 if (this.ClientSideStrongPassword(pwd)) {
21434 } else if (this.ClientSideMediumPassword(pwd)) {
21436 } else if (this.ClientSideWeakPassword(pwd)) {
21442 Roo.log('strength1: ' + strength);
21444 //var pm = this.trigger.child('div/div/div').dom;
21445 var pm = this.trigger.child('div/div');
21446 pm.removeClass(this.meterClass);
21447 pm.addClass(this.meterClass[strength]);
21450 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21452 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21454 this._lastPwd = pwd;
21458 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21460 this._lastPwd = '';
21462 var pm = this.trigger.child('div/div');
21463 pm.removeClass(this.meterClass);
21464 pm.addClass('roo-password-meter-grey');
21467 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21470 this.inputEl().dom.type='password';
21473 validateValue: function (value)
21476 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21479 if (value.length == 0) {
21480 if (this.allowBlank) {
21481 this.clearInvalid();
21485 this.markInvalid(this.errors.PwdEmpty);
21486 this.errorMsg = this.errors.PwdEmpty;
21494 if ('[\x21-\x7e]*'.match(value)) {
21495 this.markInvalid(this.errors.PwdBadChar);
21496 this.errorMsg = this.errors.PwdBadChar;
21499 if (value.length < 6) {
21500 this.markInvalid(this.errors.PwdShort);
21501 this.errorMsg = this.errors.PwdShort;
21504 if (value.length > 16) {
21505 this.markInvalid(this.errors.PwdLong);
21506 this.errorMsg = this.errors.PwdLong;
21510 if (this.ClientSideStrongPassword(value)) {
21512 } else if (this.ClientSideMediumPassword(value)) {
21514 } else if (this.ClientSideWeakPassword(value)) {
21521 if (strength < 2) {
21522 //this.markInvalid(this.errors.TooWeak);
21523 this.errorMsg = this.errors.TooWeak;
21528 console.log('strength2: ' + strength);
21530 //var pm = this.trigger.child('div/div/div').dom;
21532 var pm = this.trigger.child('div/div');
21533 pm.removeClass(this.meterClass);
21534 pm.addClass(this.meterClass[strength]);
21536 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21538 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21540 this.errorMsg = '';
21544 CharacterSetChecks: function (type)
21547 this.fResult = false;
21550 isctype: function (character, type)
21553 case this.kCapitalLetter:
21554 if (character >= 'A' && character <= 'Z') {
21559 case this.kSmallLetter:
21560 if (character >= 'a' && character <= 'z') {
21566 if (character >= '0' && character <= '9') {
21571 case this.kPunctuation:
21572 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21583 IsLongEnough: function (pwd, size)
21585 return !(pwd == null || isNaN(size) || pwd.length < size);
21588 SpansEnoughCharacterSets: function (word, nb)
21590 if (!this.IsLongEnough(word, nb))
21595 var characterSetChecks = new Array(
21596 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21597 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21600 for (var index = 0; index < word.length; ++index) {
21601 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21602 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21603 characterSetChecks[nCharSet].fResult = true;
21610 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21611 if (characterSetChecks[nCharSet].fResult) {
21616 if (nCharSets < nb) {
21622 ClientSideStrongPassword: function (pwd)
21624 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21627 ClientSideMediumPassword: function (pwd)
21629 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21632 ClientSideWeakPassword: function (pwd)
21634 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21637 })//<script type="text/javascript">
21640 * Based Ext JS Library 1.1.1
21641 * Copyright(c) 2006-2007, Ext JS, LLC.
21647 * @class Roo.HtmlEditorCore
21648 * @extends Roo.Component
21649 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21651 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21654 Roo.HtmlEditorCore = function(config){
21657 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21662 * @event initialize
21663 * Fires when the editor is fully initialized (including the iframe)
21664 * @param {Roo.HtmlEditorCore} this
21669 * Fires when the editor is first receives the focus. Any insertion must wait
21670 * until after this event.
21671 * @param {Roo.HtmlEditorCore} this
21675 * @event beforesync
21676 * Fires before the textarea is updated with content from the editor iframe. Return false
21677 * to cancel the sync.
21678 * @param {Roo.HtmlEditorCore} this
21679 * @param {String} html
21683 * @event beforepush
21684 * Fires before the iframe editor is updated with content from the textarea. Return false
21685 * to cancel the push.
21686 * @param {Roo.HtmlEditorCore} this
21687 * @param {String} html
21692 * Fires when the textarea is updated with content from the editor iframe.
21693 * @param {Roo.HtmlEditorCore} this
21694 * @param {String} html
21699 * Fires when the iframe editor is updated with content from the textarea.
21700 * @param {Roo.HtmlEditorCore} this
21701 * @param {String} html
21706 * @event editorevent
21707 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21708 * @param {Roo.HtmlEditorCore} this
21714 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21716 // defaults : white / black...
21717 this.applyBlacklists();
21724 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21728 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21734 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21739 * @cfg {Number} height (in pixels)
21743 * @cfg {Number} width (in pixels)
21748 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21751 stylesheets: false,
21756 // private properties
21757 validationEvent : false,
21759 initialized : false,
21761 sourceEditMode : false,
21762 onFocus : Roo.emptyFn,
21764 hideMode:'offsets',
21768 // blacklist + whitelisted elements..
21775 * Protected method that will not generally be called directly. It
21776 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21777 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21779 getDocMarkup : function(){
21783 // inherit styels from page...??
21784 if (this.stylesheets === false) {
21786 Roo.get(document.head).select('style').each(function(node) {
21787 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21790 Roo.get(document.head).select('link').each(function(node) {
21791 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21794 } else if (!this.stylesheets.length) {
21796 st = '<style type="text/css">' +
21797 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21800 st = '<style type="text/css">' +
21805 st += '<style type="text/css">' +
21806 'IMG { cursor: pointer } ' +
21809 var cls = 'roo-htmleditor-body';
21811 if(this.bodyCls.length){
21812 cls += ' ' + this.bodyCls;
21815 return '<html><head>' + st +
21816 //<style type="text/css">' +
21817 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21819 ' </head><body class="' + cls + '"></body></html>';
21823 onRender : function(ct, position)
21826 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21827 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21830 this.el.dom.style.border = '0 none';
21831 this.el.dom.setAttribute('tabIndex', -1);
21832 this.el.addClass('x-hidden hide');
21836 if(Roo.isIE){ // fix IE 1px bogus margin
21837 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21841 this.frameId = Roo.id();
21845 var iframe = this.owner.wrap.createChild({
21847 cls: 'form-control', // bootstrap..
21849 name: this.frameId,
21850 frameBorder : 'no',
21851 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21856 this.iframe = iframe.dom;
21858 this.assignDocWin();
21860 this.doc.designMode = 'on';
21863 this.doc.write(this.getDocMarkup());
21867 var task = { // must defer to wait for browser to be ready
21869 //console.log("run task?" + this.doc.readyState);
21870 this.assignDocWin();
21871 if(this.doc.body || this.doc.readyState == 'complete'){
21873 this.doc.designMode="on";
21877 Roo.TaskMgr.stop(task);
21878 this.initEditor.defer(10, this);
21885 Roo.TaskMgr.start(task);
21890 onResize : function(w, h)
21892 Roo.log('resize: ' +w + ',' + h );
21893 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21897 if(typeof w == 'number'){
21899 this.iframe.style.width = w + 'px';
21901 if(typeof h == 'number'){
21903 this.iframe.style.height = h + 'px';
21905 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21912 * Toggles the editor between standard and source edit mode.
21913 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21915 toggleSourceEdit : function(sourceEditMode){
21917 this.sourceEditMode = sourceEditMode === true;
21919 if(this.sourceEditMode){
21921 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21924 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21925 //this.iframe.className = '';
21928 //this.setSize(this.owner.wrap.getSize());
21929 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21936 * Protected method that will not generally be called directly. If you need/want
21937 * custom HTML cleanup, this is the method you should override.
21938 * @param {String} html The HTML to be cleaned
21939 * return {String} The cleaned HTML
21941 cleanHtml : function(html){
21942 html = String(html);
21943 if(html.length > 5){
21944 if(Roo.isSafari){ // strip safari nonsense
21945 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21948 if(html == ' '){
21955 * HTML Editor -> Textarea
21956 * Protected method that will not generally be called directly. Syncs the contents
21957 * of the editor iframe with the textarea.
21959 syncValue : function(){
21960 if(this.initialized){
21961 var bd = (this.doc.body || this.doc.documentElement);
21962 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21963 var html = bd.innerHTML;
21965 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21966 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21968 html = '<div style="'+m[0]+'">' + html + '</div>';
21971 html = this.cleanHtml(html);
21972 // fix up the special chars.. normaly like back quotes in word...
21973 // however we do not want to do this with chinese..
21974 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21975 var cc = b.charCodeAt();
21977 (cc >= 0x4E00 && cc < 0xA000 ) ||
21978 (cc >= 0x3400 && cc < 0x4E00 ) ||
21979 (cc >= 0xf900 && cc < 0xfb00 )
21985 if(this.owner.fireEvent('beforesync', this, html) !== false){
21986 this.el.dom.value = html;
21987 this.owner.fireEvent('sync', this, html);
21993 * Protected method that will not generally be called directly. Pushes the value of the textarea
21994 * into the iframe editor.
21996 pushValue : function(){
21997 if(this.initialized){
21998 var v = this.el.dom.value.trim();
22000 // if(v.length < 1){
22004 if(this.owner.fireEvent('beforepush', this, v) !== false){
22005 var d = (this.doc.body || this.doc.documentElement);
22007 this.cleanUpPaste();
22008 this.el.dom.value = d.innerHTML;
22009 this.owner.fireEvent('push', this, v);
22015 deferFocus : function(){
22016 this.focus.defer(10, this);
22020 focus : function(){
22021 if(this.win && !this.sourceEditMode){
22028 assignDocWin: function()
22030 var iframe = this.iframe;
22033 this.doc = iframe.contentWindow.document;
22034 this.win = iframe.contentWindow;
22036 // if (!Roo.get(this.frameId)) {
22039 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22040 // this.win = Roo.get(this.frameId).dom.contentWindow;
22042 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22046 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22047 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22052 initEditor : function(){
22053 //console.log("INIT EDITOR");
22054 this.assignDocWin();
22058 this.doc.designMode="on";
22060 this.doc.write(this.getDocMarkup());
22063 var dbody = (this.doc.body || this.doc.documentElement);
22064 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22065 // this copies styles from the containing element into thsi one..
22066 // not sure why we need all of this..
22067 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22069 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22070 //ss['background-attachment'] = 'fixed'; // w3c
22071 dbody.bgProperties = 'fixed'; // ie
22072 //Roo.DomHelper.applyStyles(dbody, ss);
22073 Roo.EventManager.on(this.doc, {
22074 //'mousedown': this.onEditorEvent,
22075 'mouseup': this.onEditorEvent,
22076 'dblclick': this.onEditorEvent,
22077 'click': this.onEditorEvent,
22078 'keyup': this.onEditorEvent,
22083 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22085 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22086 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22088 this.initialized = true;
22090 this.owner.fireEvent('initialize', this);
22095 onDestroy : function(){
22101 //for (var i =0; i < this.toolbars.length;i++) {
22102 // // fixme - ask toolbars for heights?
22103 // this.toolbars[i].onDestroy();
22106 //this.wrap.dom.innerHTML = '';
22107 //this.wrap.remove();
22112 onFirstFocus : function(){
22114 this.assignDocWin();
22117 this.activated = true;
22120 if(Roo.isGecko){ // prevent silly gecko errors
22122 var s = this.win.getSelection();
22123 if(!s.focusNode || s.focusNode.nodeType != 3){
22124 var r = s.getRangeAt(0);
22125 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22130 this.execCmd('useCSS', true);
22131 this.execCmd('styleWithCSS', false);
22134 this.owner.fireEvent('activate', this);
22138 adjustFont: function(btn){
22139 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22140 //if(Roo.isSafari){ // safari
22143 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22144 if(Roo.isSafari){ // safari
22145 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22146 v = (v < 10) ? 10 : v;
22147 v = (v > 48) ? 48 : v;
22148 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22153 v = Math.max(1, v+adjust);
22155 this.execCmd('FontSize', v );
22158 onEditorEvent : function(e)
22160 this.owner.fireEvent('editorevent', this, e);
22161 // this.updateToolbar();
22162 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22165 insertTag : function(tg)
22167 // could be a bit smarter... -> wrap the current selected tRoo..
22168 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22170 range = this.createRange(this.getSelection());
22171 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22172 wrappingNode.appendChild(range.extractContents());
22173 range.insertNode(wrappingNode);
22180 this.execCmd("formatblock", tg);
22184 insertText : function(txt)
22188 var range = this.createRange();
22189 range.deleteContents();
22190 //alert(Sender.getAttribute('label'));
22192 range.insertNode(this.doc.createTextNode(txt));
22198 * Executes a Midas editor command on the editor document and performs necessary focus and
22199 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22200 * @param {String} cmd The Midas command
22201 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22203 relayCmd : function(cmd, value){
22205 this.execCmd(cmd, value);
22206 this.owner.fireEvent('editorevent', this);
22207 //this.updateToolbar();
22208 this.owner.deferFocus();
22212 * Executes a Midas editor command directly on the editor document.
22213 * For visual commands, you should use {@link #relayCmd} instead.
22214 * <b>This should only be called after the editor is initialized.</b>
22215 * @param {String} cmd The Midas command
22216 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22218 execCmd : function(cmd, value){
22219 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22226 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22228 * @param {String} text | dom node..
22230 insertAtCursor : function(text)
22233 if(!this.activated){
22239 var r = this.doc.selection.createRange();
22250 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22254 // from jquery ui (MIT licenced)
22256 var win = this.win;
22258 if (win.getSelection && win.getSelection().getRangeAt) {
22259 range = win.getSelection().getRangeAt(0);
22260 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22261 range.insertNode(node);
22262 } else if (win.document.selection && win.document.selection.createRange) {
22263 // no firefox support
22264 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22265 win.document.selection.createRange().pasteHTML(txt);
22267 // no firefox support
22268 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22269 this.execCmd('InsertHTML', txt);
22278 mozKeyPress : function(e){
22280 var c = e.getCharCode(), cmd;
22283 c = String.fromCharCode(c).toLowerCase();
22297 this.cleanUpPaste.defer(100, this);
22305 e.preventDefault();
22313 fixKeys : function(){ // load time branching for fastest keydown performance
22315 return function(e){
22316 var k = e.getKey(), r;
22319 r = this.doc.selection.createRange();
22322 r.pasteHTML('    ');
22329 r = this.doc.selection.createRange();
22331 var target = r.parentElement();
22332 if(!target || target.tagName.toLowerCase() != 'li'){
22334 r.pasteHTML('<br />');
22340 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22341 this.cleanUpPaste.defer(100, this);
22347 }else if(Roo.isOpera){
22348 return function(e){
22349 var k = e.getKey();
22353 this.execCmd('InsertHTML','    ');
22356 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22357 this.cleanUpPaste.defer(100, this);
22362 }else if(Roo.isSafari){
22363 return function(e){
22364 var k = e.getKey();
22368 this.execCmd('InsertText','\t');
22372 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22373 this.cleanUpPaste.defer(100, this);
22381 getAllAncestors: function()
22383 var p = this.getSelectedNode();
22386 a.push(p); // push blank onto stack..
22387 p = this.getParentElement();
22391 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22395 a.push(this.doc.body);
22399 lastSelNode : false,
22402 getSelection : function()
22404 this.assignDocWin();
22405 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22408 getSelectedNode: function()
22410 // this may only work on Gecko!!!
22412 // should we cache this!!!!
22417 var range = this.createRange(this.getSelection()).cloneRange();
22420 var parent = range.parentElement();
22422 var testRange = range.duplicate();
22423 testRange.moveToElementText(parent);
22424 if (testRange.inRange(range)) {
22427 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22430 parent = parent.parentElement;
22435 // is ancestor a text element.
22436 var ac = range.commonAncestorContainer;
22437 if (ac.nodeType == 3) {
22438 ac = ac.parentNode;
22441 var ar = ac.childNodes;
22444 var other_nodes = [];
22445 var has_other_nodes = false;
22446 for (var i=0;i<ar.length;i++) {
22447 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22450 // fullly contained node.
22452 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22457 // probably selected..
22458 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22459 other_nodes.push(ar[i]);
22463 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22468 has_other_nodes = true;
22470 if (!nodes.length && other_nodes.length) {
22471 nodes= other_nodes;
22473 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22479 createRange: function(sel)
22481 // this has strange effects when using with
22482 // top toolbar - not sure if it's a great idea.
22483 //this.editor.contentWindow.focus();
22484 if (typeof sel != "undefined") {
22486 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22488 return this.doc.createRange();
22491 return this.doc.createRange();
22494 getParentElement: function()
22497 this.assignDocWin();
22498 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22500 var range = this.createRange(sel);
22503 var p = range.commonAncestorContainer;
22504 while (p.nodeType == 3) { // text node
22515 * Range intersection.. the hard stuff...
22519 * [ -- selected range --- ]
22523 * if end is before start or hits it. fail.
22524 * if start is after end or hits it fail.
22526 * if either hits (but other is outside. - then it's not
22532 // @see http://www.thismuchiknow.co.uk/?p=64.
22533 rangeIntersectsNode : function(range, node)
22535 var nodeRange = node.ownerDocument.createRange();
22537 nodeRange.selectNode(node);
22539 nodeRange.selectNodeContents(node);
22542 var rangeStartRange = range.cloneRange();
22543 rangeStartRange.collapse(true);
22545 var rangeEndRange = range.cloneRange();
22546 rangeEndRange.collapse(false);
22548 var nodeStartRange = nodeRange.cloneRange();
22549 nodeStartRange.collapse(true);
22551 var nodeEndRange = nodeRange.cloneRange();
22552 nodeEndRange.collapse(false);
22554 return rangeStartRange.compareBoundaryPoints(
22555 Range.START_TO_START, nodeEndRange) == -1 &&
22556 rangeEndRange.compareBoundaryPoints(
22557 Range.START_TO_START, nodeStartRange) == 1;
22561 rangeCompareNode : function(range, node)
22563 var nodeRange = node.ownerDocument.createRange();
22565 nodeRange.selectNode(node);
22567 nodeRange.selectNodeContents(node);
22571 range.collapse(true);
22573 nodeRange.collapse(true);
22575 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22576 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22578 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22580 var nodeIsBefore = ss == 1;
22581 var nodeIsAfter = ee == -1;
22583 if (nodeIsBefore && nodeIsAfter) {
22586 if (!nodeIsBefore && nodeIsAfter) {
22587 return 1; //right trailed.
22590 if (nodeIsBefore && !nodeIsAfter) {
22591 return 2; // left trailed.
22597 // private? - in a new class?
22598 cleanUpPaste : function()
22600 // cleans up the whole document..
22601 Roo.log('cleanuppaste');
22603 this.cleanUpChildren(this.doc.body);
22604 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22605 if (clean != this.doc.body.innerHTML) {
22606 this.doc.body.innerHTML = clean;
22611 cleanWordChars : function(input) {// change the chars to hex code
22612 var he = Roo.HtmlEditorCore;
22614 var output = input;
22615 Roo.each(he.swapCodes, function(sw) {
22616 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22618 output = output.replace(swapper, sw[1]);
22625 cleanUpChildren : function (n)
22627 if (!n.childNodes.length) {
22630 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22631 this.cleanUpChild(n.childNodes[i]);
22638 cleanUpChild : function (node)
22641 //console.log(node);
22642 if (node.nodeName == "#text") {
22643 // clean up silly Windows -- stuff?
22646 if (node.nodeName == "#comment") {
22647 node.parentNode.removeChild(node);
22648 // clean up silly Windows -- stuff?
22651 var lcname = node.tagName.toLowerCase();
22652 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22653 // whitelist of tags..
22655 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22657 node.parentNode.removeChild(node);
22662 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22664 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22665 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22667 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22668 // remove_keep_children = true;
22671 if (remove_keep_children) {
22672 this.cleanUpChildren(node);
22673 // inserts everything just before this node...
22674 while (node.childNodes.length) {
22675 var cn = node.childNodes[0];
22676 node.removeChild(cn);
22677 node.parentNode.insertBefore(cn, node);
22679 node.parentNode.removeChild(node);
22683 if (!node.attributes || !node.attributes.length) {
22684 this.cleanUpChildren(node);
22688 function cleanAttr(n,v)
22691 if (v.match(/^\./) || v.match(/^\//)) {
22694 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22697 if (v.match(/^#/)) {
22700 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22701 node.removeAttribute(n);
22705 var cwhite = this.cwhite;
22706 var cblack = this.cblack;
22708 function cleanStyle(n,v)
22710 if (v.match(/expression/)) { //XSS?? should we even bother..
22711 node.removeAttribute(n);
22715 var parts = v.split(/;/);
22718 Roo.each(parts, function(p) {
22719 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22723 var l = p.split(':').shift().replace(/\s+/g,'');
22724 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22726 if ( cwhite.length && cblack.indexOf(l) > -1) {
22727 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22728 //node.removeAttribute(n);
22732 // only allow 'c whitelisted system attributes'
22733 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22734 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22735 //node.removeAttribute(n);
22745 if (clean.length) {
22746 node.setAttribute(n, clean.join(';'));
22748 node.removeAttribute(n);
22754 for (var i = node.attributes.length-1; i > -1 ; i--) {
22755 var a = node.attributes[i];
22758 if (a.name.toLowerCase().substr(0,2)=='on') {
22759 node.removeAttribute(a.name);
22762 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22763 node.removeAttribute(a.name);
22766 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22767 cleanAttr(a.name,a.value); // fixme..
22770 if (a.name == 'style') {
22771 cleanStyle(a.name,a.value);
22774 /// clean up MS crap..
22775 // tecnically this should be a list of valid class'es..
22778 if (a.name == 'class') {
22779 if (a.value.match(/^Mso/)) {
22780 node.className = '';
22783 if (a.value.match(/^body$/)) {
22784 node.className = '';
22795 this.cleanUpChildren(node);
22801 * Clean up MS wordisms...
22803 cleanWord : function(node)
22808 this.cleanWord(this.doc.body);
22811 if (node.nodeName == "#text") {
22812 // clean up silly Windows -- stuff?
22815 if (node.nodeName == "#comment") {
22816 node.parentNode.removeChild(node);
22817 // clean up silly Windows -- stuff?
22821 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22822 node.parentNode.removeChild(node);
22826 // remove - but keep children..
22827 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22828 while (node.childNodes.length) {
22829 var cn = node.childNodes[0];
22830 node.removeChild(cn);
22831 node.parentNode.insertBefore(cn, node);
22833 node.parentNode.removeChild(node);
22834 this.iterateChildren(node, this.cleanWord);
22838 if (node.className.length) {
22840 var cn = node.className.split(/\W+/);
22842 Roo.each(cn, function(cls) {
22843 if (cls.match(/Mso[a-zA-Z]+/)) {
22848 node.className = cna.length ? cna.join(' ') : '';
22850 node.removeAttribute("class");
22854 if (node.hasAttribute("lang")) {
22855 node.removeAttribute("lang");
22858 if (node.hasAttribute("style")) {
22860 var styles = node.getAttribute("style").split(";");
22862 Roo.each(styles, function(s) {
22863 if (!s.match(/:/)) {
22866 var kv = s.split(":");
22867 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22870 // what ever is left... we allow.
22873 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22874 if (!nstyle.length) {
22875 node.removeAttribute('style');
22878 this.iterateChildren(node, this.cleanWord);
22884 * iterateChildren of a Node, calling fn each time, using this as the scole..
22885 * @param {DomNode} node node to iterate children of.
22886 * @param {Function} fn method of this class to call on each item.
22888 iterateChildren : function(node, fn)
22890 if (!node.childNodes.length) {
22893 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22894 fn.call(this, node.childNodes[i])
22900 * cleanTableWidths.
22902 * Quite often pasting from word etc.. results in tables with column and widths.
22903 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22906 cleanTableWidths : function(node)
22911 this.cleanTableWidths(this.doc.body);
22916 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22919 Roo.log(node.tagName);
22920 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22921 this.iterateChildren(node, this.cleanTableWidths);
22924 if (node.hasAttribute('width')) {
22925 node.removeAttribute('width');
22929 if (node.hasAttribute("style")) {
22932 var styles = node.getAttribute("style").split(";");
22934 Roo.each(styles, function(s) {
22935 if (!s.match(/:/)) {
22938 var kv = s.split(":");
22939 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22942 // what ever is left... we allow.
22945 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22946 if (!nstyle.length) {
22947 node.removeAttribute('style');
22951 this.iterateChildren(node, this.cleanTableWidths);
22959 domToHTML : function(currentElement, depth, nopadtext) {
22961 depth = depth || 0;
22962 nopadtext = nopadtext || false;
22964 if (!currentElement) {
22965 return this.domToHTML(this.doc.body);
22968 //Roo.log(currentElement);
22970 var allText = false;
22971 var nodeName = currentElement.nodeName;
22972 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22974 if (nodeName == '#text') {
22976 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22981 if (nodeName != 'BODY') {
22984 // Prints the node tagName, such as <A>, <IMG>, etc
22987 for(i = 0; i < currentElement.attributes.length;i++) {
22989 var aname = currentElement.attributes.item(i).name;
22990 if (!currentElement.attributes.item(i).value.length) {
22993 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22996 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23005 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23008 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23013 // Traverse the tree
23015 var currentElementChild = currentElement.childNodes.item(i);
23016 var allText = true;
23017 var innerHTML = '';
23019 while (currentElementChild) {
23020 // Formatting code (indent the tree so it looks nice on the screen)
23021 var nopad = nopadtext;
23022 if (lastnode == 'SPAN') {
23026 if (currentElementChild.nodeName == '#text') {
23027 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23028 toadd = nopadtext ? toadd : toadd.trim();
23029 if (!nopad && toadd.length > 80) {
23030 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23032 innerHTML += toadd;
23035 currentElementChild = currentElement.childNodes.item(i);
23041 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23043 // Recursively traverse the tree structure of the child node
23044 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23045 lastnode = currentElementChild.nodeName;
23047 currentElementChild=currentElement.childNodes.item(i);
23053 // The remaining code is mostly for formatting the tree
23054 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23059 ret+= "</"+tagName+">";
23065 applyBlacklists : function()
23067 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23068 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23072 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23073 if (b.indexOf(tag) > -1) {
23076 this.white.push(tag);
23080 Roo.each(w, function(tag) {
23081 if (b.indexOf(tag) > -1) {
23084 if (this.white.indexOf(tag) > -1) {
23087 this.white.push(tag);
23092 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23093 if (w.indexOf(tag) > -1) {
23096 this.black.push(tag);
23100 Roo.each(b, function(tag) {
23101 if (w.indexOf(tag) > -1) {
23104 if (this.black.indexOf(tag) > -1) {
23107 this.black.push(tag);
23112 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23113 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23117 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23118 if (b.indexOf(tag) > -1) {
23121 this.cwhite.push(tag);
23125 Roo.each(w, function(tag) {
23126 if (b.indexOf(tag) > -1) {
23129 if (this.cwhite.indexOf(tag) > -1) {
23132 this.cwhite.push(tag);
23137 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23138 if (w.indexOf(tag) > -1) {
23141 this.cblack.push(tag);
23145 Roo.each(b, function(tag) {
23146 if (w.indexOf(tag) > -1) {
23149 if (this.cblack.indexOf(tag) > -1) {
23152 this.cblack.push(tag);
23157 setStylesheets : function(stylesheets)
23159 if(typeof(stylesheets) == 'string'){
23160 Roo.get(this.iframe.contentDocument.head).createChild({
23162 rel : 'stylesheet',
23171 Roo.each(stylesheets, function(s) {
23176 Roo.get(_this.iframe.contentDocument.head).createChild({
23178 rel : 'stylesheet',
23187 removeStylesheets : function()
23191 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23196 setStyle : function(style)
23198 Roo.get(this.iframe.contentDocument.head).createChild({
23207 // hide stuff that is not compatible
23221 * @event specialkey
23225 * @cfg {String} fieldClass @hide
23228 * @cfg {String} focusClass @hide
23231 * @cfg {String} autoCreate @hide
23234 * @cfg {String} inputType @hide
23237 * @cfg {String} invalidClass @hide
23240 * @cfg {String} invalidText @hide
23243 * @cfg {String} msgFx @hide
23246 * @cfg {String} validateOnBlur @hide
23250 Roo.HtmlEditorCore.white = [
23251 'area', 'br', 'img', 'input', 'hr', 'wbr',
23253 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23254 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23255 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23256 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23257 'table', 'ul', 'xmp',
23259 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23262 'dir', 'menu', 'ol', 'ul', 'dl',
23268 Roo.HtmlEditorCore.black = [
23269 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23271 'base', 'basefont', 'bgsound', 'blink', 'body',
23272 'frame', 'frameset', 'head', 'html', 'ilayer',
23273 'iframe', 'layer', 'link', 'meta', 'object',
23274 'script', 'style' ,'title', 'xml' // clean later..
23276 Roo.HtmlEditorCore.clean = [
23277 'script', 'style', 'title', 'xml'
23279 Roo.HtmlEditorCore.remove = [
23284 Roo.HtmlEditorCore.ablack = [
23288 Roo.HtmlEditorCore.aclean = [
23289 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23293 Roo.HtmlEditorCore.pwhite= [
23294 'http', 'https', 'mailto'
23297 // white listed style attributes.
23298 Roo.HtmlEditorCore.cwhite= [
23299 // 'text-align', /// default is to allow most things..
23305 // black listed style attributes.
23306 Roo.HtmlEditorCore.cblack= [
23307 // 'font-size' -- this can be set by the project
23311 Roo.HtmlEditorCore.swapCodes =[
23330 * @class Roo.bootstrap.HtmlEditor
23331 * @extends Roo.bootstrap.TextArea
23332 * Bootstrap HtmlEditor class
23335 * Create a new HtmlEditor
23336 * @param {Object} config The config object
23339 Roo.bootstrap.HtmlEditor = function(config){
23340 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23341 if (!this.toolbars) {
23342 this.toolbars = [];
23345 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23348 * @event initialize
23349 * Fires when the editor is fully initialized (including the iframe)
23350 * @param {HtmlEditor} this
23355 * Fires when the editor is first receives the focus. Any insertion must wait
23356 * until after this event.
23357 * @param {HtmlEditor} this
23361 * @event beforesync
23362 * Fires before the textarea is updated with content from the editor iframe. Return false
23363 * to cancel the sync.
23364 * @param {HtmlEditor} this
23365 * @param {String} html
23369 * @event beforepush
23370 * Fires before the iframe editor is updated with content from the textarea. Return false
23371 * to cancel the push.
23372 * @param {HtmlEditor} this
23373 * @param {String} html
23378 * Fires when the textarea is updated with content from the editor iframe.
23379 * @param {HtmlEditor} this
23380 * @param {String} html
23385 * Fires when the iframe editor is updated with content from the textarea.
23386 * @param {HtmlEditor} this
23387 * @param {String} html
23391 * @event editmodechange
23392 * Fires when the editor switches edit modes
23393 * @param {HtmlEditor} this
23394 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23396 editmodechange: true,
23398 * @event editorevent
23399 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23400 * @param {HtmlEditor} this
23404 * @event firstfocus
23405 * Fires when on first focus - needed by toolbars..
23406 * @param {HtmlEditor} this
23411 * Auto save the htmlEditor value as a file into Events
23412 * @param {HtmlEditor} this
23416 * @event savedpreview
23417 * preview the saved version of htmlEditor
23418 * @param {HtmlEditor} this
23425 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23429 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23434 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23439 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23444 * @cfg {Number} height (in pixels)
23448 * @cfg {Number} width (in pixels)
23453 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23456 stylesheets: false,
23461 // private properties
23462 validationEvent : false,
23464 initialized : false,
23467 onFocus : Roo.emptyFn,
23469 hideMode:'offsets',
23471 tbContainer : false,
23475 toolbarContainer :function() {
23476 return this.wrap.select('.x-html-editor-tb',true).first();
23480 * Protected method that will not generally be called directly. It
23481 * is called when the editor creates its toolbar. Override this method if you need to
23482 * add custom toolbar buttons.
23483 * @param {HtmlEditor} editor
23485 createToolbar : function(){
23486 Roo.log('renewing');
23487 Roo.log("create toolbars");
23489 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23490 this.toolbars[0].render(this.toolbarContainer());
23494 // if (!editor.toolbars || !editor.toolbars.length) {
23495 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23498 // for (var i =0 ; i < editor.toolbars.length;i++) {
23499 // editor.toolbars[i] = Roo.factory(
23500 // typeof(editor.toolbars[i]) == 'string' ?
23501 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23502 // Roo.bootstrap.HtmlEditor);
23503 // editor.toolbars[i].init(editor);
23509 onRender : function(ct, position)
23511 // Roo.log("Call onRender: " + this.xtype);
23513 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23515 this.wrap = this.inputEl().wrap({
23516 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23519 this.editorcore.onRender(ct, position);
23521 if (this.resizable) {
23522 this.resizeEl = new Roo.Resizable(this.wrap, {
23526 minHeight : this.height,
23527 height: this.height,
23528 handles : this.resizable,
23531 resize : function(r, w, h) {
23532 _t.onResize(w,h); // -something
23538 this.createToolbar(this);
23541 if(!this.width && this.resizable){
23542 this.setSize(this.wrap.getSize());
23544 if (this.resizeEl) {
23545 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23546 // should trigger onReize..
23552 onResize : function(w, h)
23554 Roo.log('resize: ' +w + ',' + h );
23555 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23559 if(this.inputEl() ){
23560 if(typeof w == 'number'){
23561 var aw = w - this.wrap.getFrameWidth('lr');
23562 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23565 if(typeof h == 'number'){
23566 var tbh = -11; // fixme it needs to tool bar size!
23567 for (var i =0; i < this.toolbars.length;i++) {
23568 // fixme - ask toolbars for heights?
23569 tbh += this.toolbars[i].el.getHeight();
23570 //if (this.toolbars[i].footer) {
23571 // tbh += this.toolbars[i].footer.el.getHeight();
23579 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23580 ah -= 5; // knock a few pixes off for look..
23581 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23585 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23586 this.editorcore.onResize(ew,eh);
23591 * Toggles the editor between standard and source edit mode.
23592 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23594 toggleSourceEdit : function(sourceEditMode)
23596 this.editorcore.toggleSourceEdit(sourceEditMode);
23598 if(this.editorcore.sourceEditMode){
23599 Roo.log('editor - showing textarea');
23602 // Roo.log(this.syncValue());
23604 this.inputEl().removeClass(['hide', 'x-hidden']);
23605 this.inputEl().dom.removeAttribute('tabIndex');
23606 this.inputEl().focus();
23608 Roo.log('editor - hiding textarea');
23610 // Roo.log(this.pushValue());
23613 this.inputEl().addClass(['hide', 'x-hidden']);
23614 this.inputEl().dom.setAttribute('tabIndex', -1);
23615 //this.deferFocus();
23618 if(this.resizable){
23619 this.setSize(this.wrap.getSize());
23622 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23625 // private (for BoxComponent)
23626 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23628 // private (for BoxComponent)
23629 getResizeEl : function(){
23633 // private (for BoxComponent)
23634 getPositionEl : function(){
23639 initEvents : function(){
23640 this.originalValue = this.getValue();
23644 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23647 // markInvalid : Roo.emptyFn,
23649 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23652 // clearInvalid : Roo.emptyFn,
23654 setValue : function(v){
23655 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23656 this.editorcore.pushValue();
23661 deferFocus : function(){
23662 this.focus.defer(10, this);
23666 focus : function(){
23667 this.editorcore.focus();
23673 onDestroy : function(){
23679 for (var i =0; i < this.toolbars.length;i++) {
23680 // fixme - ask toolbars for heights?
23681 this.toolbars[i].onDestroy();
23684 this.wrap.dom.innerHTML = '';
23685 this.wrap.remove();
23690 onFirstFocus : function(){
23691 //Roo.log("onFirstFocus");
23692 this.editorcore.onFirstFocus();
23693 for (var i =0; i < this.toolbars.length;i++) {
23694 this.toolbars[i].onFirstFocus();
23700 syncValue : function()
23702 this.editorcore.syncValue();
23705 pushValue : function()
23707 this.editorcore.pushValue();
23711 // hide stuff that is not compatible
23725 * @event specialkey
23729 * @cfg {String} fieldClass @hide
23732 * @cfg {String} focusClass @hide
23735 * @cfg {String} autoCreate @hide
23738 * @cfg {String} inputType @hide
23741 * @cfg {String} invalidClass @hide
23744 * @cfg {String} invalidText @hide
23747 * @cfg {String} msgFx @hide
23750 * @cfg {String} validateOnBlur @hide
23759 Roo.namespace('Roo.bootstrap.htmleditor');
23761 * @class Roo.bootstrap.HtmlEditorToolbar1
23766 new Roo.bootstrap.HtmlEditor({
23769 new Roo.bootstrap.HtmlEditorToolbar1({
23770 disable : { fonts: 1 , format: 1, ..., ... , ...],
23776 * @cfg {Object} disable List of elements to disable..
23777 * @cfg {Array} btns List of additional buttons.
23781 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23784 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23787 Roo.apply(this, config);
23789 // default disabled, based on 'good practice'..
23790 this.disable = this.disable || {};
23791 Roo.applyIf(this.disable, {
23794 specialElements : true
23796 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23798 this.editor = config.editor;
23799 this.editorcore = config.editor.editorcore;
23801 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23803 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23804 // dont call parent... till later.
23806 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23811 editorcore : false,
23816 "h1","h2","h3","h4","h5","h6",
23818 "abbr", "acronym", "address", "cite", "samp", "var",
23822 onRender : function(ct, position)
23824 // Roo.log("Call onRender: " + this.xtype);
23826 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23828 this.el.dom.style.marginBottom = '0';
23830 var editorcore = this.editorcore;
23831 var editor= this.editor;
23834 var btn = function(id,cmd , toggle, handler, html){
23836 var event = toggle ? 'toggle' : 'click';
23841 xns: Roo.bootstrap,
23844 enableToggle:toggle !== false,
23846 pressed : toggle ? false : null,
23849 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23850 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23856 // var cb_box = function...
23861 xns: Roo.bootstrap,
23862 glyphicon : 'font',
23866 xns: Roo.bootstrap,
23870 Roo.each(this.formats, function(f) {
23871 style.menu.items.push({
23873 xns: Roo.bootstrap,
23874 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23879 editorcore.insertTag(this.tagname);
23886 children.push(style);
23888 btn('bold',false,true);
23889 btn('italic',false,true);
23890 btn('align-left', 'justifyleft',true);
23891 btn('align-center', 'justifycenter',true);
23892 btn('align-right' , 'justifyright',true);
23893 btn('link', false, false, function(btn) {
23894 //Roo.log("create link?");
23895 var url = prompt(this.createLinkText, this.defaultLinkValue);
23896 if(url && url != 'http:/'+'/'){
23897 this.editorcore.relayCmd('createlink', url);
23900 btn('list','insertunorderedlist',true);
23901 btn('pencil', false,true, function(btn){
23903 this.toggleSourceEdit(btn.pressed);
23906 if (this.editor.btns.length > 0) {
23907 for (var i = 0; i<this.editor.btns.length; i++) {
23908 children.push(this.editor.btns[i]);
23916 xns: Roo.bootstrap,
23921 xns: Roo.bootstrap,
23926 cog.menu.items.push({
23928 xns: Roo.bootstrap,
23929 html : Clean styles,
23934 editorcore.insertTag(this.tagname);
23943 this.xtype = 'NavSimplebar';
23945 for(var i=0;i< children.length;i++) {
23947 this.buttons.add(this.addxtypeChild(children[i]));
23951 editor.on('editorevent', this.updateToolbar, this);
23953 onBtnClick : function(id)
23955 this.editorcore.relayCmd(id);
23956 this.editorcore.focus();
23960 * Protected method that will not generally be called directly. It triggers
23961 * a toolbar update by reading the markup state of the current selection in the editor.
23963 updateToolbar: function(){
23965 if(!this.editorcore.activated){
23966 this.editor.onFirstFocus(); // is this neeed?
23970 var btns = this.buttons;
23971 var doc = this.editorcore.doc;
23972 btns.get('bold').setActive(doc.queryCommandState('bold'));
23973 btns.get('italic').setActive(doc.queryCommandState('italic'));
23974 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23976 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23977 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23978 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23980 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23981 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23984 var ans = this.editorcore.getAllAncestors();
23985 if (this.formatCombo) {
23988 var store = this.formatCombo.store;
23989 this.formatCombo.setValue("");
23990 for (var i =0; i < ans.length;i++) {
23991 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23993 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24001 // hides menus... - so this cant be on a menu...
24002 Roo.bootstrap.MenuMgr.hideAll();
24004 Roo.bootstrap.MenuMgr.hideAll();
24005 //this.editorsyncValue();
24007 onFirstFocus: function() {
24008 this.buttons.each(function(item){
24012 toggleSourceEdit : function(sourceEditMode){
24015 if(sourceEditMode){
24016 Roo.log("disabling buttons");
24017 this.buttons.each( function(item){
24018 if(item.cmd != 'pencil'){
24024 Roo.log("enabling buttons");
24025 if(this.editorcore.initialized){
24026 this.buttons.each( function(item){
24032 Roo.log("calling toggole on editor");
24033 // tell the editor that it's been pressed..
24034 this.editor.toggleSourceEdit(sourceEditMode);
24044 * @class Roo.bootstrap.Table.AbstractSelectionModel
24045 * @extends Roo.util.Observable
24046 * Abstract base class for grid SelectionModels. It provides the interface that should be
24047 * implemented by descendant classes. This class should not be directly instantiated.
24050 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24051 this.locked = false;
24052 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24056 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24057 /** @ignore Called by the grid automatically. Do not call directly. */
24058 init : function(grid){
24064 * Locks the selections.
24067 this.locked = true;
24071 * Unlocks the selections.
24073 unlock : function(){
24074 this.locked = false;
24078 * Returns true if the selections are locked.
24079 * @return {Boolean}
24081 isLocked : function(){
24082 return this.locked;
24086 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24087 * @class Roo.bootstrap.Table.RowSelectionModel
24088 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24089 * It supports multiple selections and keyboard selection/navigation.
24091 * @param {Object} config
24094 Roo.bootstrap.Table.RowSelectionModel = function(config){
24095 Roo.apply(this, config);
24096 this.selections = new Roo.util.MixedCollection(false, function(o){
24101 this.lastActive = false;
24105 * @event selectionchange
24106 * Fires when the selection changes
24107 * @param {SelectionModel} this
24109 "selectionchange" : true,
24111 * @event afterselectionchange
24112 * Fires after the selection changes (eg. by key press or clicking)
24113 * @param {SelectionModel} this
24115 "afterselectionchange" : true,
24117 * @event beforerowselect
24118 * Fires when a row is selected being selected, return false to cancel.
24119 * @param {SelectionModel} this
24120 * @param {Number} rowIndex The selected index
24121 * @param {Boolean} keepExisting False if other selections will be cleared
24123 "beforerowselect" : true,
24126 * Fires when a row is selected.
24127 * @param {SelectionModel} this
24128 * @param {Number} rowIndex The selected index
24129 * @param {Roo.data.Record} r The record
24131 "rowselect" : true,
24133 * @event rowdeselect
24134 * Fires when a row is deselected.
24135 * @param {SelectionModel} this
24136 * @param {Number} rowIndex The selected index
24138 "rowdeselect" : true
24140 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24141 this.locked = false;
24144 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24146 * @cfg {Boolean} singleSelect
24147 * True to allow selection of only one row at a time (defaults to false)
24149 singleSelect : false,
24152 initEvents : function()
24155 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24156 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24157 //}else{ // allow click to work like normal
24158 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24160 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24161 this.grid.on("rowclick", this.handleMouseDown, this);
24163 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24164 "up" : function(e){
24166 this.selectPrevious(e.shiftKey);
24167 }else if(this.last !== false && this.lastActive !== false){
24168 var last = this.last;
24169 this.selectRange(this.last, this.lastActive-1);
24170 this.grid.getView().focusRow(this.lastActive);
24171 if(last !== false){
24175 this.selectFirstRow();
24177 this.fireEvent("afterselectionchange", this);
24179 "down" : function(e){
24181 this.selectNext(e.shiftKey);
24182 }else if(this.last !== false && this.lastActive !== false){
24183 var last = this.last;
24184 this.selectRange(this.last, this.lastActive+1);
24185 this.grid.getView().focusRow(this.lastActive);
24186 if(last !== false){
24190 this.selectFirstRow();
24192 this.fireEvent("afterselectionchange", this);
24196 this.grid.store.on('load', function(){
24197 this.selections.clear();
24200 var view = this.grid.view;
24201 view.on("refresh", this.onRefresh, this);
24202 view.on("rowupdated", this.onRowUpdated, this);
24203 view.on("rowremoved", this.onRemove, this);
24208 onRefresh : function()
24210 var ds = this.grid.store, i, v = this.grid.view;
24211 var s = this.selections;
24212 s.each(function(r){
24213 if((i = ds.indexOfId(r.id)) != -1){
24222 onRemove : function(v, index, r){
24223 this.selections.remove(r);
24227 onRowUpdated : function(v, index, r){
24228 if(this.isSelected(r)){
24229 v.onRowSelect(index);
24235 * @param {Array} records The records to select
24236 * @param {Boolean} keepExisting (optional) True to keep existing selections
24238 selectRecords : function(records, keepExisting)
24241 this.clearSelections();
24243 var ds = this.grid.store;
24244 for(var i = 0, len = records.length; i < len; i++){
24245 this.selectRow(ds.indexOf(records[i]), true);
24250 * Gets the number of selected rows.
24253 getCount : function(){
24254 return this.selections.length;
24258 * Selects the first row in the grid.
24260 selectFirstRow : function(){
24265 * Select the last row.
24266 * @param {Boolean} keepExisting (optional) True to keep existing selections
24268 selectLastRow : function(keepExisting){
24269 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24270 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24274 * Selects the row immediately following the last selected row.
24275 * @param {Boolean} keepExisting (optional) True to keep existing selections
24277 selectNext : function(keepExisting)
24279 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24280 this.selectRow(this.last+1, keepExisting);
24281 this.grid.getView().focusRow(this.last);
24286 * Selects the row that precedes the last selected row.
24287 * @param {Boolean} keepExisting (optional) True to keep existing selections
24289 selectPrevious : function(keepExisting){
24291 this.selectRow(this.last-1, keepExisting);
24292 this.grid.getView().focusRow(this.last);
24297 * Returns the selected records
24298 * @return {Array} Array of selected records
24300 getSelections : function(){
24301 return [].concat(this.selections.items);
24305 * Returns the first selected record.
24308 getSelected : function(){
24309 return this.selections.itemAt(0);
24314 * Clears all selections.
24316 clearSelections : function(fast)
24322 var ds = this.grid.store;
24323 var s = this.selections;
24324 s.each(function(r){
24325 this.deselectRow(ds.indexOfId(r.id));
24329 this.selections.clear();
24336 * Selects all rows.
24338 selectAll : function(){
24342 this.selections.clear();
24343 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24344 this.selectRow(i, true);
24349 * Returns True if there is a selection.
24350 * @return {Boolean}
24352 hasSelection : function(){
24353 return this.selections.length > 0;
24357 * Returns True if the specified row is selected.
24358 * @param {Number/Record} record The record or index of the record to check
24359 * @return {Boolean}
24361 isSelected : function(index){
24362 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24363 return (r && this.selections.key(r.id) ? true : false);
24367 * Returns True if the specified record id is selected.
24368 * @param {String} id The id of record to check
24369 * @return {Boolean}
24371 isIdSelected : function(id){
24372 return (this.selections.key(id) ? true : false);
24377 handleMouseDBClick : function(e, t){
24381 handleMouseDown : function(e, t)
24383 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24384 if(this.isLocked() || rowIndex < 0 ){
24387 if(e.shiftKey && this.last !== false){
24388 var last = this.last;
24389 this.selectRange(last, rowIndex, e.ctrlKey);
24390 this.last = last; // reset the last
24394 var isSelected = this.isSelected(rowIndex);
24395 //Roo.log("select row:" + rowIndex);
24397 this.deselectRow(rowIndex);
24399 this.selectRow(rowIndex, true);
24403 if(e.button !== 0 && isSelected){
24404 alert('rowIndex 2: ' + rowIndex);
24405 view.focusRow(rowIndex);
24406 }else if(e.ctrlKey && isSelected){
24407 this.deselectRow(rowIndex);
24408 }else if(!isSelected){
24409 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24410 view.focusRow(rowIndex);
24414 this.fireEvent("afterselectionchange", this);
24417 handleDragableRowClick : function(grid, rowIndex, e)
24419 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24420 this.selectRow(rowIndex, false);
24421 grid.view.focusRow(rowIndex);
24422 this.fireEvent("afterselectionchange", this);
24427 * Selects multiple rows.
24428 * @param {Array} rows Array of the indexes of the row to select
24429 * @param {Boolean} keepExisting (optional) True to keep existing selections
24431 selectRows : function(rows, keepExisting){
24433 this.clearSelections();
24435 for(var i = 0, len = rows.length; i < len; i++){
24436 this.selectRow(rows[i], true);
24441 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24442 * @param {Number} startRow The index of the first row in the range
24443 * @param {Number} endRow The index of the last row in the range
24444 * @param {Boolean} keepExisting (optional) True to retain existing selections
24446 selectRange : function(startRow, endRow, keepExisting){
24451 this.clearSelections();
24453 if(startRow <= endRow){
24454 for(var i = startRow; i <= endRow; i++){
24455 this.selectRow(i, true);
24458 for(var i = startRow; i >= endRow; i--){
24459 this.selectRow(i, true);
24465 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24466 * @param {Number} startRow The index of the first row in the range
24467 * @param {Number} endRow The index of the last row in the range
24469 deselectRange : function(startRow, endRow, preventViewNotify){
24473 for(var i = startRow; i <= endRow; i++){
24474 this.deselectRow(i, preventViewNotify);
24480 * @param {Number} row The index of the row to select
24481 * @param {Boolean} keepExisting (optional) True to keep existing selections
24483 selectRow : function(index, keepExisting, preventViewNotify)
24485 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24488 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24489 if(!keepExisting || this.singleSelect){
24490 this.clearSelections();
24493 var r = this.grid.store.getAt(index);
24494 //console.log('selectRow - record id :' + r.id);
24496 this.selections.add(r);
24497 this.last = this.lastActive = index;
24498 if(!preventViewNotify){
24499 var proxy = new Roo.Element(
24500 this.grid.getRowDom(index)
24502 proxy.addClass('bg-info info');
24504 this.fireEvent("rowselect", this, index, r);
24505 this.fireEvent("selectionchange", this);
24511 * @param {Number} row The index of the row to deselect
24513 deselectRow : function(index, preventViewNotify)
24518 if(this.last == index){
24521 if(this.lastActive == index){
24522 this.lastActive = false;
24525 var r = this.grid.store.getAt(index);
24530 this.selections.remove(r);
24531 //.console.log('deselectRow - record id :' + r.id);
24532 if(!preventViewNotify){
24534 var proxy = new Roo.Element(
24535 this.grid.getRowDom(index)
24537 proxy.removeClass('bg-info info');
24539 this.fireEvent("rowdeselect", this, index);
24540 this.fireEvent("selectionchange", this);
24544 restoreLast : function(){
24546 this.last = this._last;
24551 acceptsNav : function(row, col, cm){
24552 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24556 onEditorKey : function(field, e){
24557 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24562 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24564 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24566 }else if(k == e.ENTER && !e.ctrlKey){
24570 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24572 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24574 }else if(k == e.ESC){
24578 g.startEditing(newCell[0], newCell[1]);
24584 * Ext JS Library 1.1.1
24585 * Copyright(c) 2006-2007, Ext JS, LLC.
24587 * Originally Released Under LGPL - original licence link has changed is not relivant.
24590 * <script type="text/javascript">
24594 * @class Roo.bootstrap.PagingToolbar
24595 * @extends Roo.bootstrap.NavSimplebar
24596 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24598 * Create a new PagingToolbar
24599 * @param {Object} config The config object
24600 * @param {Roo.data.Store} store
24602 Roo.bootstrap.PagingToolbar = function(config)
24604 // old args format still supported... - xtype is prefered..
24605 // created from xtype...
24607 this.ds = config.dataSource;
24609 if (config.store && !this.ds) {
24610 this.store= Roo.factory(config.store, Roo.data);
24611 this.ds = this.store;
24612 this.ds.xmodule = this.xmodule || false;
24615 this.toolbarItems = [];
24616 if (config.items) {
24617 this.toolbarItems = config.items;
24620 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24625 this.bind(this.ds);
24628 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24632 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24634 * @cfg {Roo.data.Store} dataSource
24635 * The underlying data store providing the paged data
24638 * @cfg {String/HTMLElement/Element} container
24639 * container The id or element that will contain the toolbar
24642 * @cfg {Boolean} displayInfo
24643 * True to display the displayMsg (defaults to false)
24646 * @cfg {Number} pageSize
24647 * The number of records to display per page (defaults to 20)
24651 * @cfg {String} displayMsg
24652 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24654 displayMsg : 'Displaying {0} - {1} of {2}',
24656 * @cfg {String} emptyMsg
24657 * The message to display when no records are found (defaults to "No data to display")
24659 emptyMsg : 'No data to display',
24661 * Customizable piece of the default paging text (defaults to "Page")
24664 beforePageText : "Page",
24666 * Customizable piece of the default paging text (defaults to "of %0")
24669 afterPageText : "of {0}",
24671 * Customizable piece of the default paging text (defaults to "First Page")
24674 firstText : "First Page",
24676 * Customizable piece of the default paging text (defaults to "Previous Page")
24679 prevText : "Previous Page",
24681 * Customizable piece of the default paging text (defaults to "Next Page")
24684 nextText : "Next Page",
24686 * Customizable piece of the default paging text (defaults to "Last Page")
24689 lastText : "Last Page",
24691 * Customizable piece of the default paging text (defaults to "Refresh")
24694 refreshText : "Refresh",
24698 onRender : function(ct, position)
24700 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24701 this.navgroup.parentId = this.id;
24702 this.navgroup.onRender(this.el, null);
24703 // add the buttons to the navgroup
24705 if(this.displayInfo){
24706 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24707 this.displayEl = this.el.select('.x-paging-info', true).first();
24708 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24709 // this.displayEl = navel.el.select('span',true).first();
24715 Roo.each(_this.buttons, function(e){ // this might need to use render????
24716 Roo.factory(e).render(_this.el);
24720 Roo.each(_this.toolbarItems, function(e) {
24721 _this.navgroup.addItem(e);
24725 this.first = this.navgroup.addItem({
24726 tooltip: this.firstText,
24728 icon : 'fa fa-backward',
24730 preventDefault: true,
24731 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24734 this.prev = this.navgroup.addItem({
24735 tooltip: this.prevText,
24737 icon : 'fa fa-step-backward',
24739 preventDefault: true,
24740 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24742 //this.addSeparator();
24745 var field = this.navgroup.addItem( {
24747 cls : 'x-paging-position',
24749 html : this.beforePageText +
24750 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24751 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24754 this.field = field.el.select('input', true).first();
24755 this.field.on("keydown", this.onPagingKeydown, this);
24756 this.field.on("focus", function(){this.dom.select();});
24759 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24760 //this.field.setHeight(18);
24761 //this.addSeparator();
24762 this.next = this.navgroup.addItem({
24763 tooltip: this.nextText,
24765 html : ' <i class="fa fa-step-forward">',
24767 preventDefault: true,
24768 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24770 this.last = this.navgroup.addItem({
24771 tooltip: this.lastText,
24772 icon : 'fa fa-forward',
24775 preventDefault: true,
24776 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24778 //this.addSeparator();
24779 this.loading = this.navgroup.addItem({
24780 tooltip: this.refreshText,
24781 icon: 'fa fa-refresh',
24782 preventDefault: true,
24783 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24789 updateInfo : function(){
24790 if(this.displayEl){
24791 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24792 var msg = count == 0 ?
24796 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24798 this.displayEl.update(msg);
24803 onLoad : function(ds, r, o)
24805 this.cursor = o.params.start ? o.params.start : 0;
24807 var d = this.getPageData(),
24812 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24813 this.field.dom.value = ap;
24814 this.first.setDisabled(ap == 1);
24815 this.prev.setDisabled(ap == 1);
24816 this.next.setDisabled(ap == ps);
24817 this.last.setDisabled(ap == ps);
24818 this.loading.enable();
24823 getPageData : function(){
24824 var total = this.ds.getTotalCount();
24827 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24828 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24833 onLoadError : function(){
24834 this.loading.enable();
24838 onPagingKeydown : function(e){
24839 var k = e.getKey();
24840 var d = this.getPageData();
24842 var v = this.field.dom.value, pageNum;
24843 if(!v || isNaN(pageNum = parseInt(v, 10))){
24844 this.field.dom.value = d.activePage;
24847 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24848 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24851 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))
24853 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24854 this.field.dom.value = pageNum;
24855 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24858 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24860 var v = this.field.dom.value, pageNum;
24861 var increment = (e.shiftKey) ? 10 : 1;
24862 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24865 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24866 this.field.dom.value = d.activePage;
24869 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24871 this.field.dom.value = parseInt(v, 10) + increment;
24872 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24873 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24880 beforeLoad : function(){
24882 this.loading.disable();
24887 onClick : function(which){
24896 ds.load({params:{start: 0, limit: this.pageSize}});
24899 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24902 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24905 var total = ds.getTotalCount();
24906 var extra = total % this.pageSize;
24907 var lastStart = extra ? (total - extra) : total-this.pageSize;
24908 ds.load({params:{start: lastStart, limit: this.pageSize}});
24911 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24917 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24918 * @param {Roo.data.Store} store The data store to unbind
24920 unbind : function(ds){
24921 ds.un("beforeload", this.beforeLoad, this);
24922 ds.un("load", this.onLoad, this);
24923 ds.un("loadexception", this.onLoadError, this);
24924 ds.un("remove", this.updateInfo, this);
24925 ds.un("add", this.updateInfo, this);
24926 this.ds = undefined;
24930 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24931 * @param {Roo.data.Store} store The data store to bind
24933 bind : function(ds){
24934 ds.on("beforeload", this.beforeLoad, this);
24935 ds.on("load", this.onLoad, this);
24936 ds.on("loadexception", this.onLoadError, this);
24937 ds.on("remove", this.updateInfo, this);
24938 ds.on("add", this.updateInfo, this);
24949 * @class Roo.bootstrap.MessageBar
24950 * @extends Roo.bootstrap.Component
24951 * Bootstrap MessageBar class
24952 * @cfg {String} html contents of the MessageBar
24953 * @cfg {String} weight (info | success | warning | danger) default info
24954 * @cfg {String} beforeClass insert the bar before the given class
24955 * @cfg {Boolean} closable (true | false) default false
24956 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24959 * Create a new Element
24960 * @param {Object} config The config object
24963 Roo.bootstrap.MessageBar = function(config){
24964 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24967 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24973 beforeClass: 'bootstrap-sticky-wrap',
24975 getAutoCreate : function(){
24979 cls: 'alert alert-dismissable alert-' + this.weight,
24984 html: this.html || ''
24990 cfg.cls += ' alert-messages-fixed';
25004 onRender : function(ct, position)
25006 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25009 var cfg = Roo.apply({}, this.getAutoCreate());
25013 cfg.cls += ' ' + this.cls;
25016 cfg.style = this.style;
25018 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25020 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25023 this.el.select('>button.close').on('click', this.hide, this);
25029 if (!this.rendered) {
25035 this.fireEvent('show', this);
25041 if (!this.rendered) {
25047 this.fireEvent('hide', this);
25050 update : function()
25052 // var e = this.el.dom.firstChild;
25054 // if(this.closable){
25055 // e = e.nextSibling;
25058 // e.data = this.html || '';
25060 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25076 * @class Roo.bootstrap.Graph
25077 * @extends Roo.bootstrap.Component
25078 * Bootstrap Graph class
25082 @cfg {String} graphtype bar | vbar | pie
25083 @cfg {number} g_x coodinator | centre x (pie)
25084 @cfg {number} g_y coodinator | centre y (pie)
25085 @cfg {number} g_r radius (pie)
25086 @cfg {number} g_height height of the chart (respected by all elements in the set)
25087 @cfg {number} g_width width of the chart (respected by all elements in the set)
25088 @cfg {Object} title The title of the chart
25091 -opts (object) options for the chart
25093 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25094 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25096 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.
25097 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25099 o stretch (boolean)
25101 -opts (object) options for the pie
25104 o startAngle (number)
25105 o endAngle (number)
25109 * Create a new Input
25110 * @param {Object} config The config object
25113 Roo.bootstrap.Graph = function(config){
25114 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25120 * The img click event for the img.
25121 * @param {Roo.EventObject} e
25127 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25138 //g_colors: this.colors,
25145 getAutoCreate : function(){
25156 onRender : function(ct,position){
25159 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25161 if (typeof(Raphael) == 'undefined') {
25162 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25166 this.raphael = Raphael(this.el.dom);
25168 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25169 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25170 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25171 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25173 r.text(160, 10, "Single Series Chart").attr(txtattr);
25174 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25175 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25176 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25178 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25179 r.barchart(330, 10, 300, 220, data1);
25180 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25181 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25184 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25185 // r.barchart(30, 30, 560, 250, xdata, {
25186 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25187 // axis : "0 0 1 1",
25188 // axisxlabels : xdata
25189 // //yvalues : cols,
25192 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25194 // this.load(null,xdata,{
25195 // axis : "0 0 1 1",
25196 // axisxlabels : xdata
25201 load : function(graphtype,xdata,opts)
25203 this.raphael.clear();
25205 graphtype = this.graphtype;
25210 var r = this.raphael,
25211 fin = function () {
25212 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25214 fout = function () {
25215 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25217 pfin = function() {
25218 this.sector.stop();
25219 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25222 this.label[0].stop();
25223 this.label[0].attr({ r: 7.5 });
25224 this.label[1].attr({ "font-weight": 800 });
25227 pfout = function() {
25228 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25231 this.label[0].animate({ r: 5 }, 500, "bounce");
25232 this.label[1].attr({ "font-weight": 400 });
25238 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25241 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25244 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25245 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25247 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25254 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25259 setTitle: function(o)
25264 initEvents: function() {
25267 this.el.on('click', this.onClick, this);
25271 onClick : function(e)
25273 Roo.log('img onclick');
25274 this.fireEvent('click', this, e);
25286 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25289 * @class Roo.bootstrap.dash.NumberBox
25290 * @extends Roo.bootstrap.Component
25291 * Bootstrap NumberBox class
25292 * @cfg {String} headline Box headline
25293 * @cfg {String} content Box content
25294 * @cfg {String} icon Box icon
25295 * @cfg {String} footer Footer text
25296 * @cfg {String} fhref Footer href
25299 * Create a new NumberBox
25300 * @param {Object} config The config object
25304 Roo.bootstrap.dash.NumberBox = function(config){
25305 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25309 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25318 getAutoCreate : function(){
25322 cls : 'small-box ',
25330 cls : 'roo-headline',
25331 html : this.headline
25335 cls : 'roo-content',
25336 html : this.content
25350 cls : 'ion ' + this.icon
25359 cls : 'small-box-footer',
25360 href : this.fhref || '#',
25364 cfg.cn.push(footer);
25371 onRender : function(ct,position){
25372 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25379 setHeadline: function (value)
25381 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25384 setFooter: function (value, href)
25386 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25389 this.el.select('a.small-box-footer',true).first().attr('href', href);
25394 setContent: function (value)
25396 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25399 initEvents: function()
25413 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25416 * @class Roo.bootstrap.dash.TabBox
25417 * @extends Roo.bootstrap.Component
25418 * Bootstrap TabBox class
25419 * @cfg {String} title Title of the TabBox
25420 * @cfg {String} icon Icon of the TabBox
25421 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25422 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25425 * Create a new TabBox
25426 * @param {Object} config The config object
25430 Roo.bootstrap.dash.TabBox = function(config){
25431 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25436 * When a pane is added
25437 * @param {Roo.bootstrap.dash.TabPane} pane
25441 * @event activatepane
25442 * When a pane is activated
25443 * @param {Roo.bootstrap.dash.TabPane} pane
25445 "activatepane" : true
25453 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25458 tabScrollable : false,
25460 getChildContainer : function()
25462 return this.el.select('.tab-content', true).first();
25465 getAutoCreate : function(){
25469 cls: 'pull-left header',
25477 cls: 'fa ' + this.icon
25483 cls: 'nav nav-tabs pull-right',
25489 if(this.tabScrollable){
25496 cls: 'nav nav-tabs pull-right',
25507 cls: 'nav-tabs-custom',
25512 cls: 'tab-content no-padding',
25520 initEvents : function()
25522 //Roo.log('add add pane handler');
25523 this.on('addpane', this.onAddPane, this);
25526 * Updates the box title
25527 * @param {String} html to set the title to.
25529 setTitle : function(value)
25531 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25533 onAddPane : function(pane)
25535 this.panes.push(pane);
25536 //Roo.log('addpane');
25538 // tabs are rendere left to right..
25539 if(!this.showtabs){
25543 var ctr = this.el.select('.nav-tabs', true).first();
25546 var existing = ctr.select('.nav-tab',true);
25547 var qty = existing.getCount();;
25550 var tab = ctr.createChild({
25552 cls : 'nav-tab' + (qty ? '' : ' active'),
25560 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25563 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25565 pane.el.addClass('active');
25570 onTabClick : function(ev,un,ob,pane)
25572 //Roo.log('tab - prev default');
25573 ev.preventDefault();
25576 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25577 pane.tab.addClass('active');
25578 //Roo.log(pane.title);
25579 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25580 // technically we should have a deactivate event.. but maybe add later.
25581 // and it should not de-activate the selected tab...
25582 this.fireEvent('activatepane', pane);
25583 pane.el.addClass('active');
25584 pane.fireEvent('activate');
25589 getActivePane : function()
25592 Roo.each(this.panes, function(p) {
25593 if(p.el.hasClass('active')){
25614 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25616 * @class Roo.bootstrap.TabPane
25617 * @extends Roo.bootstrap.Component
25618 * Bootstrap TabPane class
25619 * @cfg {Boolean} active (false | true) Default false
25620 * @cfg {String} title title of panel
25624 * Create a new TabPane
25625 * @param {Object} config The config object
25628 Roo.bootstrap.dash.TabPane = function(config){
25629 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25635 * When a pane is activated
25636 * @param {Roo.bootstrap.dash.TabPane} pane
25643 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25648 // the tabBox that this is attached to.
25651 getAutoCreate : function()
25659 cfg.cls += ' active';
25664 initEvents : function()
25666 //Roo.log('trigger add pane handler');
25667 this.parent().fireEvent('addpane', this)
25671 * Updates the tab title
25672 * @param {String} html to set the title to.
25674 setTitle: function(str)
25680 this.tab.select('a', true).first().dom.innerHTML = str;
25697 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25700 * @class Roo.bootstrap.menu.Menu
25701 * @extends Roo.bootstrap.Component
25702 * Bootstrap Menu class - container for Menu
25703 * @cfg {String} html Text of the menu
25704 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25705 * @cfg {String} icon Font awesome icon
25706 * @cfg {String} pos Menu align to (top | bottom) default bottom
25710 * Create a new Menu
25711 * @param {Object} config The config object
25715 Roo.bootstrap.menu.Menu = function(config){
25716 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25720 * @event beforeshow
25721 * Fires before this menu is displayed
25722 * @param {Roo.bootstrap.menu.Menu} this
25726 * @event beforehide
25727 * Fires before this menu is hidden
25728 * @param {Roo.bootstrap.menu.Menu} this
25733 * Fires after this menu is displayed
25734 * @param {Roo.bootstrap.menu.Menu} this
25739 * Fires after this menu is hidden
25740 * @param {Roo.bootstrap.menu.Menu} this
25745 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25746 * @param {Roo.bootstrap.menu.Menu} this
25747 * @param {Roo.EventObject} e
25754 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25758 weight : 'default',
25763 getChildContainer : function() {
25764 if(this.isSubMenu){
25768 return this.el.select('ul.dropdown-menu', true).first();
25771 getAutoCreate : function()
25776 cls : 'roo-menu-text',
25784 cls : 'fa ' + this.icon
25795 cls : 'dropdown-button btn btn-' + this.weight,
25800 cls : 'dropdown-toggle btn btn-' + this.weight,
25810 cls : 'dropdown-menu'
25816 if(this.pos == 'top'){
25817 cfg.cls += ' dropup';
25820 if(this.isSubMenu){
25823 cls : 'dropdown-menu'
25830 onRender : function(ct, position)
25832 this.isSubMenu = ct.hasClass('dropdown-submenu');
25834 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25837 initEvents : function()
25839 if(this.isSubMenu){
25843 this.hidden = true;
25845 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25846 this.triggerEl.on('click', this.onTriggerPress, this);
25848 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25849 this.buttonEl.on('click', this.onClick, this);
25855 if(this.isSubMenu){
25859 return this.el.select('ul.dropdown-menu', true).first();
25862 onClick : function(e)
25864 this.fireEvent("click", this, e);
25867 onTriggerPress : function(e)
25869 if (this.isVisible()) {
25876 isVisible : function(){
25877 return !this.hidden;
25882 this.fireEvent("beforeshow", this);
25884 this.hidden = false;
25885 this.el.addClass('open');
25887 Roo.get(document).on("mouseup", this.onMouseUp, this);
25889 this.fireEvent("show", this);
25896 this.fireEvent("beforehide", this);
25898 this.hidden = true;
25899 this.el.removeClass('open');
25901 Roo.get(document).un("mouseup", this.onMouseUp);
25903 this.fireEvent("hide", this);
25906 onMouseUp : function()
25920 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25923 * @class Roo.bootstrap.menu.Item
25924 * @extends Roo.bootstrap.Component
25925 * Bootstrap MenuItem class
25926 * @cfg {Boolean} submenu (true | false) default false
25927 * @cfg {String} html text of the item
25928 * @cfg {String} href the link
25929 * @cfg {Boolean} disable (true | false) default false
25930 * @cfg {Boolean} preventDefault (true | false) default true
25931 * @cfg {String} icon Font awesome icon
25932 * @cfg {String} pos Submenu align to (left | right) default right
25936 * Create a new Item
25937 * @param {Object} config The config object
25941 Roo.bootstrap.menu.Item = function(config){
25942 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25946 * Fires when the mouse is hovering over this menu
25947 * @param {Roo.bootstrap.menu.Item} this
25948 * @param {Roo.EventObject} e
25953 * Fires when the mouse exits this menu
25954 * @param {Roo.bootstrap.menu.Item} this
25955 * @param {Roo.EventObject} e
25961 * The raw click event for the entire grid.
25962 * @param {Roo.EventObject} e
25968 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25973 preventDefault: true,
25978 getAutoCreate : function()
25983 cls : 'roo-menu-item-text',
25991 cls : 'fa ' + this.icon
26000 href : this.href || '#',
26007 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26011 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26013 if(this.pos == 'left'){
26014 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26021 initEvents : function()
26023 this.el.on('mouseover', this.onMouseOver, this);
26024 this.el.on('mouseout', this.onMouseOut, this);
26026 this.el.select('a', true).first().on('click', this.onClick, this);
26030 onClick : function(e)
26032 if(this.preventDefault){
26033 e.preventDefault();
26036 this.fireEvent("click", this, e);
26039 onMouseOver : function(e)
26041 if(this.submenu && this.pos == 'left'){
26042 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26045 this.fireEvent("mouseover", this, e);
26048 onMouseOut : function(e)
26050 this.fireEvent("mouseout", this, e);
26062 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26065 * @class Roo.bootstrap.menu.Separator
26066 * @extends Roo.bootstrap.Component
26067 * Bootstrap Separator class
26070 * Create a new Separator
26071 * @param {Object} config The config object
26075 Roo.bootstrap.menu.Separator = function(config){
26076 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26079 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26081 getAutoCreate : function(){
26102 * @class Roo.bootstrap.Tooltip
26103 * Bootstrap Tooltip class
26104 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26105 * to determine which dom element triggers the tooltip.
26107 * It needs to add support for additional attributes like tooltip-position
26110 * Create a new Toolti
26111 * @param {Object} config The config object
26114 Roo.bootstrap.Tooltip = function(config){
26115 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26117 this.alignment = Roo.bootstrap.Tooltip.alignment;
26119 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26120 this.alignment = config.alignment;
26125 Roo.apply(Roo.bootstrap.Tooltip, {
26127 * @function init initialize tooltip monitoring.
26131 currentTip : false,
26132 currentRegion : false,
26138 Roo.get(document).on('mouseover', this.enter ,this);
26139 Roo.get(document).on('mouseout', this.leave, this);
26142 this.currentTip = new Roo.bootstrap.Tooltip();
26145 enter : function(ev)
26147 var dom = ev.getTarget();
26149 //Roo.log(['enter',dom]);
26150 var el = Roo.fly(dom);
26151 if (this.currentEl) {
26153 //Roo.log(this.currentEl);
26154 //Roo.log(this.currentEl.contains(dom));
26155 if (this.currentEl == el) {
26158 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26164 if (this.currentTip.el) {
26165 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26169 if(!el || el.dom == document){
26175 // you can not look for children, as if el is the body.. then everythign is the child..
26176 if (!el.attr('tooltip')) { //
26177 if (!el.select("[tooltip]").elements.length) {
26180 // is the mouse over this child...?
26181 bindEl = el.select("[tooltip]").first();
26182 var xy = ev.getXY();
26183 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26184 //Roo.log("not in region.");
26187 //Roo.log("child element over..");
26190 this.currentEl = bindEl;
26191 this.currentTip.bind(bindEl);
26192 this.currentRegion = Roo.lib.Region.getRegion(dom);
26193 this.currentTip.enter();
26196 leave : function(ev)
26198 var dom = ev.getTarget();
26199 //Roo.log(['leave',dom]);
26200 if (!this.currentEl) {
26205 if (dom != this.currentEl.dom) {
26208 var xy = ev.getXY();
26209 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26212 // only activate leave if mouse cursor is outside... bounding box..
26217 if (this.currentTip) {
26218 this.currentTip.leave();
26220 //Roo.log('clear currentEl');
26221 this.currentEl = false;
26226 'left' : ['r-l', [-2,0], 'right'],
26227 'right' : ['l-r', [2,0], 'left'],
26228 'bottom' : ['t-b', [0,2], 'top'],
26229 'top' : [ 'b-t', [0,-2], 'bottom']
26235 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26240 delay : null, // can be { show : 300 , hide: 500}
26244 hoverState : null, //???
26246 placement : 'bottom',
26250 getAutoCreate : function(){
26257 cls : 'tooltip-arrow'
26260 cls : 'tooltip-inner'
26267 bind : function(el)
26273 enter : function () {
26275 if (this.timeout != null) {
26276 clearTimeout(this.timeout);
26279 this.hoverState = 'in';
26280 //Roo.log("enter - show");
26281 if (!this.delay || !this.delay.show) {
26286 this.timeout = setTimeout(function () {
26287 if (_t.hoverState == 'in') {
26290 }, this.delay.show);
26294 clearTimeout(this.timeout);
26296 this.hoverState = 'out';
26297 if (!this.delay || !this.delay.hide) {
26303 this.timeout = setTimeout(function () {
26304 //Roo.log("leave - timeout");
26306 if (_t.hoverState == 'out') {
26308 Roo.bootstrap.Tooltip.currentEl = false;
26313 show : function (msg)
26316 this.render(document.body);
26319 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26321 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26323 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26325 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26327 var placement = typeof this.placement == 'function' ?
26328 this.placement.call(this, this.el, on_el) :
26331 var autoToken = /\s?auto?\s?/i;
26332 var autoPlace = autoToken.test(placement);
26334 placement = placement.replace(autoToken, '') || 'top';
26338 //this.el.setXY([0,0]);
26340 //this.el.dom.style.display='block';
26342 //this.el.appendTo(on_el);
26344 var p = this.getPosition();
26345 var box = this.el.getBox();
26351 var align = this.alignment[placement];
26353 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26355 if(placement == 'top' || placement == 'bottom'){
26357 placement = 'right';
26360 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26361 placement = 'left';
26364 var scroll = Roo.select('body', true).first().getScroll();
26366 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26370 align = this.alignment[placement];
26373 this.el.alignTo(this.bindEl, align[0],align[1]);
26374 //var arrow = this.el.select('.arrow',true).first();
26375 //arrow.set(align[2],
26377 this.el.addClass(placement);
26379 this.el.addClass('in fade');
26381 this.hoverState = null;
26383 if (this.el.hasClass('fade')) {
26394 //this.el.setXY([0,0]);
26395 this.el.removeClass('in');
26411 * @class Roo.bootstrap.LocationPicker
26412 * @extends Roo.bootstrap.Component
26413 * Bootstrap LocationPicker class
26414 * @cfg {Number} latitude Position when init default 0
26415 * @cfg {Number} longitude Position when init default 0
26416 * @cfg {Number} zoom default 15
26417 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26418 * @cfg {Boolean} mapTypeControl default false
26419 * @cfg {Boolean} disableDoubleClickZoom default false
26420 * @cfg {Boolean} scrollwheel default true
26421 * @cfg {Boolean} streetViewControl default false
26422 * @cfg {Number} radius default 0
26423 * @cfg {String} locationName
26424 * @cfg {Boolean} draggable default true
26425 * @cfg {Boolean} enableAutocomplete default false
26426 * @cfg {Boolean} enableReverseGeocode default true
26427 * @cfg {String} markerTitle
26430 * Create a new LocationPicker
26431 * @param {Object} config The config object
26435 Roo.bootstrap.LocationPicker = function(config){
26437 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26442 * Fires when the picker initialized.
26443 * @param {Roo.bootstrap.LocationPicker} this
26444 * @param {Google Location} location
26448 * @event positionchanged
26449 * Fires when the picker position changed.
26450 * @param {Roo.bootstrap.LocationPicker} this
26451 * @param {Google Location} location
26453 positionchanged : true,
26456 * Fires when the map resize.
26457 * @param {Roo.bootstrap.LocationPicker} this
26462 * Fires when the map show.
26463 * @param {Roo.bootstrap.LocationPicker} this
26468 * Fires when the map hide.
26469 * @param {Roo.bootstrap.LocationPicker} this
26474 * Fires when click the map.
26475 * @param {Roo.bootstrap.LocationPicker} this
26476 * @param {Map event} e
26480 * @event mapRightClick
26481 * Fires when right click the map.
26482 * @param {Roo.bootstrap.LocationPicker} this
26483 * @param {Map event} e
26485 mapRightClick : true,
26487 * @event markerClick
26488 * Fires when click the marker.
26489 * @param {Roo.bootstrap.LocationPicker} this
26490 * @param {Map event} e
26492 markerClick : true,
26494 * @event markerRightClick
26495 * Fires when right click the marker.
26496 * @param {Roo.bootstrap.LocationPicker} this
26497 * @param {Map event} e
26499 markerRightClick : true,
26501 * @event OverlayViewDraw
26502 * Fires when OverlayView Draw
26503 * @param {Roo.bootstrap.LocationPicker} this
26505 OverlayViewDraw : true,
26507 * @event OverlayViewOnAdd
26508 * Fires when OverlayView Draw
26509 * @param {Roo.bootstrap.LocationPicker} this
26511 OverlayViewOnAdd : true,
26513 * @event OverlayViewOnRemove
26514 * Fires when OverlayView Draw
26515 * @param {Roo.bootstrap.LocationPicker} this
26517 OverlayViewOnRemove : true,
26519 * @event OverlayViewShow
26520 * Fires when OverlayView Draw
26521 * @param {Roo.bootstrap.LocationPicker} this
26522 * @param {Pixel} cpx
26524 OverlayViewShow : true,
26526 * @event OverlayViewHide
26527 * Fires when OverlayView Draw
26528 * @param {Roo.bootstrap.LocationPicker} this
26530 OverlayViewHide : true,
26532 * @event loadexception
26533 * Fires when load google lib failed.
26534 * @param {Roo.bootstrap.LocationPicker} this
26536 loadexception : true
26541 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26543 gMapContext: false,
26549 mapTypeControl: false,
26550 disableDoubleClickZoom: false,
26552 streetViewControl: false,
26556 enableAutocomplete: false,
26557 enableReverseGeocode: true,
26560 getAutoCreate: function()
26565 cls: 'roo-location-picker'
26571 initEvents: function(ct, position)
26573 if(!this.el.getWidth() || this.isApplied()){
26577 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26582 initial: function()
26584 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26585 this.fireEvent('loadexception', this);
26589 if(!this.mapTypeId){
26590 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26593 this.gMapContext = this.GMapContext();
26595 this.initOverlayView();
26597 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26601 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26602 _this.setPosition(_this.gMapContext.marker.position);
26605 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26606 _this.fireEvent('mapClick', this, event);
26610 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26611 _this.fireEvent('mapRightClick', this, event);
26615 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26616 _this.fireEvent('markerClick', this, event);
26620 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26621 _this.fireEvent('markerRightClick', this, event);
26625 this.setPosition(this.gMapContext.location);
26627 this.fireEvent('initial', this, this.gMapContext.location);
26630 initOverlayView: function()
26634 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26638 _this.fireEvent('OverlayViewDraw', _this);
26643 _this.fireEvent('OverlayViewOnAdd', _this);
26646 onRemove: function()
26648 _this.fireEvent('OverlayViewOnRemove', _this);
26651 show: function(cpx)
26653 _this.fireEvent('OverlayViewShow', _this, cpx);
26658 _this.fireEvent('OverlayViewHide', _this);
26664 fromLatLngToContainerPixel: function(event)
26666 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26669 isApplied: function()
26671 return this.getGmapContext() == false ? false : true;
26674 getGmapContext: function()
26676 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26679 GMapContext: function()
26681 var position = new google.maps.LatLng(this.latitude, this.longitude);
26683 var _map = new google.maps.Map(this.el.dom, {
26686 mapTypeId: this.mapTypeId,
26687 mapTypeControl: this.mapTypeControl,
26688 disableDoubleClickZoom: this.disableDoubleClickZoom,
26689 scrollwheel: this.scrollwheel,
26690 streetViewControl: this.streetViewControl,
26691 locationName: this.locationName,
26692 draggable: this.draggable,
26693 enableAutocomplete: this.enableAutocomplete,
26694 enableReverseGeocode: this.enableReverseGeocode
26697 var _marker = new google.maps.Marker({
26698 position: position,
26700 title: this.markerTitle,
26701 draggable: this.draggable
26708 location: position,
26709 radius: this.radius,
26710 locationName: this.locationName,
26711 addressComponents: {
26712 formatted_address: null,
26713 addressLine1: null,
26714 addressLine2: null,
26716 streetNumber: null,
26720 stateOrProvince: null
26723 domContainer: this.el.dom,
26724 geodecoder: new google.maps.Geocoder()
26728 drawCircle: function(center, radius, options)
26730 if (this.gMapContext.circle != null) {
26731 this.gMapContext.circle.setMap(null);
26735 options = Roo.apply({}, options, {
26736 strokeColor: "#0000FF",
26737 strokeOpacity: .35,
26739 fillColor: "#0000FF",
26743 options.map = this.gMapContext.map;
26744 options.radius = radius;
26745 options.center = center;
26746 this.gMapContext.circle = new google.maps.Circle(options);
26747 return this.gMapContext.circle;
26753 setPosition: function(location)
26755 this.gMapContext.location = location;
26756 this.gMapContext.marker.setPosition(location);
26757 this.gMapContext.map.panTo(location);
26758 this.drawCircle(location, this.gMapContext.radius, {});
26762 if (this.gMapContext.settings.enableReverseGeocode) {
26763 this.gMapContext.geodecoder.geocode({
26764 latLng: this.gMapContext.location
26765 }, function(results, status) {
26767 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26768 _this.gMapContext.locationName = results[0].formatted_address;
26769 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26771 _this.fireEvent('positionchanged', this, location);
26778 this.fireEvent('positionchanged', this, location);
26783 google.maps.event.trigger(this.gMapContext.map, "resize");
26785 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26787 this.fireEvent('resize', this);
26790 setPositionByLatLng: function(latitude, longitude)
26792 this.setPosition(new google.maps.LatLng(latitude, longitude));
26795 getCurrentPosition: function()
26798 latitude: this.gMapContext.location.lat(),
26799 longitude: this.gMapContext.location.lng()
26803 getAddressName: function()
26805 return this.gMapContext.locationName;
26808 getAddressComponents: function()
26810 return this.gMapContext.addressComponents;
26813 address_component_from_google_geocode: function(address_components)
26817 for (var i = 0; i < address_components.length; i++) {
26818 var component = address_components[i];
26819 if (component.types.indexOf("postal_code") >= 0) {
26820 result.postalCode = component.short_name;
26821 } else if (component.types.indexOf("street_number") >= 0) {
26822 result.streetNumber = component.short_name;
26823 } else if (component.types.indexOf("route") >= 0) {
26824 result.streetName = component.short_name;
26825 } else if (component.types.indexOf("neighborhood") >= 0) {
26826 result.city = component.short_name;
26827 } else if (component.types.indexOf("locality") >= 0) {
26828 result.city = component.short_name;
26829 } else if (component.types.indexOf("sublocality") >= 0) {
26830 result.district = component.short_name;
26831 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26832 result.stateOrProvince = component.short_name;
26833 } else if (component.types.indexOf("country") >= 0) {
26834 result.country = component.short_name;
26838 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26839 result.addressLine2 = "";
26843 setZoomLevel: function(zoom)
26845 this.gMapContext.map.setZoom(zoom);
26858 this.fireEvent('show', this);
26869 this.fireEvent('hide', this);
26874 Roo.apply(Roo.bootstrap.LocationPicker, {
26876 OverlayView : function(map, options)
26878 options = options || {};
26892 * @class Roo.bootstrap.Alert
26893 * @extends Roo.bootstrap.Component
26894 * Bootstrap Alert class
26895 * @cfg {String} title The title of alert
26896 * @cfg {String} html The content of alert
26897 * @cfg {String} weight ( success | info | warning | danger )
26898 * @cfg {String} faicon font-awesomeicon
26901 * Create a new alert
26902 * @param {Object} config The config object
26906 Roo.bootstrap.Alert = function(config){
26907 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26911 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26918 getAutoCreate : function()
26927 cls : 'roo-alert-icon'
26932 cls : 'roo-alert-title',
26937 cls : 'roo-alert-text',
26944 cfg.cn[0].cls += ' fa ' + this.faicon;
26948 cfg.cls += ' alert-' + this.weight;
26954 initEvents: function()
26956 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26959 setTitle : function(str)
26961 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26964 setText : function(str)
26966 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26969 setWeight : function(weight)
26972 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26975 this.weight = weight;
26977 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26980 setIcon : function(icon)
26983 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26986 this.faicon = icon;
26988 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27009 * @class Roo.bootstrap.UploadCropbox
27010 * @extends Roo.bootstrap.Component
27011 * Bootstrap UploadCropbox class
27012 * @cfg {String} emptyText show when image has been loaded
27013 * @cfg {String} rotateNotify show when image too small to rotate
27014 * @cfg {Number} errorTimeout default 3000
27015 * @cfg {Number} minWidth default 300
27016 * @cfg {Number} minHeight default 300
27017 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27018 * @cfg {Boolean} isDocument (true|false) default false
27019 * @cfg {String} url action url
27020 * @cfg {String} paramName default 'imageUpload'
27021 * @cfg {String} method default POST
27022 * @cfg {Boolean} loadMask (true|false) default true
27023 * @cfg {Boolean} loadingText default 'Loading...'
27026 * Create a new UploadCropbox
27027 * @param {Object} config The config object
27030 Roo.bootstrap.UploadCropbox = function(config){
27031 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27035 * @event beforeselectfile
27036 * Fire before select file
27037 * @param {Roo.bootstrap.UploadCropbox} this
27039 "beforeselectfile" : true,
27042 * Fire after initEvent
27043 * @param {Roo.bootstrap.UploadCropbox} this
27048 * Fire after initEvent
27049 * @param {Roo.bootstrap.UploadCropbox} this
27050 * @param {String} data
27055 * Fire when preparing the file data
27056 * @param {Roo.bootstrap.UploadCropbox} this
27057 * @param {Object} file
27062 * Fire when get exception
27063 * @param {Roo.bootstrap.UploadCropbox} this
27064 * @param {XMLHttpRequest} xhr
27066 "exception" : true,
27068 * @event beforeloadcanvas
27069 * Fire before load the canvas
27070 * @param {Roo.bootstrap.UploadCropbox} this
27071 * @param {String} src
27073 "beforeloadcanvas" : true,
27076 * Fire when trash image
27077 * @param {Roo.bootstrap.UploadCropbox} this
27082 * Fire when download the image
27083 * @param {Roo.bootstrap.UploadCropbox} this
27087 * @event footerbuttonclick
27088 * Fire when footerbuttonclick
27089 * @param {Roo.bootstrap.UploadCropbox} this
27090 * @param {String} type
27092 "footerbuttonclick" : true,
27096 * @param {Roo.bootstrap.UploadCropbox} this
27101 * Fire when rotate the image
27102 * @param {Roo.bootstrap.UploadCropbox} this
27103 * @param {String} pos
27108 * Fire when inspect the file
27109 * @param {Roo.bootstrap.UploadCropbox} this
27110 * @param {Object} file
27115 * Fire when xhr upload the file
27116 * @param {Roo.bootstrap.UploadCropbox} this
27117 * @param {Object} data
27122 * Fire when arrange the file data
27123 * @param {Roo.bootstrap.UploadCropbox} this
27124 * @param {Object} formData
27129 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27132 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27134 emptyText : 'Click to upload image',
27135 rotateNotify : 'Image is too small to rotate',
27136 errorTimeout : 3000,
27150 cropType : 'image/jpeg',
27152 canvasLoaded : false,
27153 isDocument : false,
27155 paramName : 'imageUpload',
27157 loadingText : 'Loading...',
27160 getAutoCreate : function()
27164 cls : 'roo-upload-cropbox',
27168 cls : 'roo-upload-cropbox-selector',
27173 cls : 'roo-upload-cropbox-body',
27174 style : 'cursor:pointer',
27178 cls : 'roo-upload-cropbox-preview'
27182 cls : 'roo-upload-cropbox-thumb'
27186 cls : 'roo-upload-cropbox-empty-notify',
27187 html : this.emptyText
27191 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27192 html : this.rotateNotify
27198 cls : 'roo-upload-cropbox-footer',
27201 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27211 onRender : function(ct, position)
27213 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27215 if (this.buttons.length) {
27217 Roo.each(this.buttons, function(bb) {
27219 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27221 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27227 this.maskEl = this.el;
27231 initEvents : function()
27233 this.urlAPI = (window.createObjectURL && window) ||
27234 (window.URL && URL.revokeObjectURL && URL) ||
27235 (window.webkitURL && webkitURL);
27237 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27238 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27240 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27241 this.selectorEl.hide();
27243 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27244 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27246 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27247 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27248 this.thumbEl.hide();
27250 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27251 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27253 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27254 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27255 this.errorEl.hide();
27257 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27258 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27259 this.footerEl.hide();
27261 this.setThumbBoxSize();
27267 this.fireEvent('initial', this);
27274 window.addEventListener("resize", function() { _this.resize(); } );
27276 this.bodyEl.on('click', this.beforeSelectFile, this);
27279 this.bodyEl.on('touchstart', this.onTouchStart, this);
27280 this.bodyEl.on('touchmove', this.onTouchMove, this);
27281 this.bodyEl.on('touchend', this.onTouchEnd, this);
27285 this.bodyEl.on('mousedown', this.onMouseDown, this);
27286 this.bodyEl.on('mousemove', this.onMouseMove, this);
27287 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27288 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27289 Roo.get(document).on('mouseup', this.onMouseUp, this);
27292 this.selectorEl.on('change', this.onFileSelected, this);
27298 this.baseScale = 1;
27300 this.baseRotate = 1;
27301 this.dragable = false;
27302 this.pinching = false;
27305 this.cropData = false;
27306 this.notifyEl.dom.innerHTML = this.emptyText;
27308 this.selectorEl.dom.value = '';
27312 resize : function()
27314 if(this.fireEvent('resize', this) != false){
27315 this.setThumbBoxPosition();
27316 this.setCanvasPosition();
27320 onFooterButtonClick : function(e, el, o, type)
27323 case 'rotate-left' :
27324 this.onRotateLeft(e);
27326 case 'rotate-right' :
27327 this.onRotateRight(e);
27330 this.beforeSelectFile(e);
27345 this.fireEvent('footerbuttonclick', this, type);
27348 beforeSelectFile : function(e)
27350 e.preventDefault();
27352 if(this.fireEvent('beforeselectfile', this) != false){
27353 this.selectorEl.dom.click();
27357 onFileSelected : function(e)
27359 e.preventDefault();
27361 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27365 var file = this.selectorEl.dom.files[0];
27367 if(this.fireEvent('inspect', this, file) != false){
27368 this.prepare(file);
27373 trash : function(e)
27375 this.fireEvent('trash', this);
27378 download : function(e)
27380 this.fireEvent('download', this);
27383 loadCanvas : function(src)
27385 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27389 this.imageEl = document.createElement('img');
27393 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27395 this.imageEl.src = src;
27399 onLoadCanvas : function()
27401 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27402 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27404 this.bodyEl.un('click', this.beforeSelectFile, this);
27406 this.notifyEl.hide();
27407 this.thumbEl.show();
27408 this.footerEl.show();
27410 this.baseRotateLevel();
27412 if(this.isDocument){
27413 this.setThumbBoxSize();
27416 this.setThumbBoxPosition();
27418 this.baseScaleLevel();
27424 this.canvasLoaded = true;
27427 this.maskEl.unmask();
27432 setCanvasPosition : function()
27434 if(!this.canvasEl){
27438 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27439 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27441 this.previewEl.setLeft(pw);
27442 this.previewEl.setTop(ph);
27446 onMouseDown : function(e)
27450 this.dragable = true;
27451 this.pinching = false;
27453 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27454 this.dragable = false;
27458 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27459 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27463 onMouseMove : function(e)
27467 if(!this.canvasLoaded){
27471 if (!this.dragable){
27475 var minX = Math.ceil(this.thumbEl.getLeft(true));
27476 var minY = Math.ceil(this.thumbEl.getTop(true));
27478 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27479 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27481 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27482 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27484 x = x - this.mouseX;
27485 y = y - this.mouseY;
27487 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27488 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27490 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27491 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27493 this.previewEl.setLeft(bgX);
27494 this.previewEl.setTop(bgY);
27496 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27497 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27500 onMouseUp : function(e)
27504 this.dragable = false;
27507 onMouseWheel : function(e)
27511 this.startScale = this.scale;
27513 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27515 if(!this.zoomable()){
27516 this.scale = this.startScale;
27525 zoomable : function()
27527 var minScale = this.thumbEl.getWidth() / this.minWidth;
27529 if(this.minWidth < this.minHeight){
27530 minScale = this.thumbEl.getHeight() / this.minHeight;
27533 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27534 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27538 (this.rotate == 0 || this.rotate == 180) &&
27540 width > this.imageEl.OriginWidth ||
27541 height > this.imageEl.OriginHeight ||
27542 (width < this.minWidth && height < this.minHeight)
27550 (this.rotate == 90 || this.rotate == 270) &&
27552 width > this.imageEl.OriginWidth ||
27553 height > this.imageEl.OriginHeight ||
27554 (width < this.minHeight && height < this.minWidth)
27561 !this.isDocument &&
27562 (this.rotate == 0 || this.rotate == 180) &&
27564 width < this.minWidth ||
27565 width > this.imageEl.OriginWidth ||
27566 height < this.minHeight ||
27567 height > this.imageEl.OriginHeight
27574 !this.isDocument &&
27575 (this.rotate == 90 || this.rotate == 270) &&
27577 width < this.minHeight ||
27578 width > this.imageEl.OriginWidth ||
27579 height < this.minWidth ||
27580 height > this.imageEl.OriginHeight
27590 onRotateLeft : function(e)
27592 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27594 var minScale = this.thumbEl.getWidth() / this.minWidth;
27596 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27597 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27599 this.startScale = this.scale;
27601 while (this.getScaleLevel() < minScale){
27603 this.scale = this.scale + 1;
27605 if(!this.zoomable()){
27610 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27611 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27616 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27623 this.scale = this.startScale;
27625 this.onRotateFail();
27630 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27632 if(this.isDocument){
27633 this.setThumbBoxSize();
27634 this.setThumbBoxPosition();
27635 this.setCanvasPosition();
27640 this.fireEvent('rotate', this, 'left');
27644 onRotateRight : function(e)
27646 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27648 var minScale = this.thumbEl.getWidth() / this.minWidth;
27650 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27651 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27653 this.startScale = this.scale;
27655 while (this.getScaleLevel() < minScale){
27657 this.scale = this.scale + 1;
27659 if(!this.zoomable()){
27664 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27665 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27670 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27677 this.scale = this.startScale;
27679 this.onRotateFail();
27684 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27686 if(this.isDocument){
27687 this.setThumbBoxSize();
27688 this.setThumbBoxPosition();
27689 this.setCanvasPosition();
27694 this.fireEvent('rotate', this, 'right');
27697 onRotateFail : function()
27699 this.errorEl.show(true);
27703 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27708 this.previewEl.dom.innerHTML = '';
27710 var canvasEl = document.createElement("canvas");
27712 var contextEl = canvasEl.getContext("2d");
27714 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27715 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27716 var center = this.imageEl.OriginWidth / 2;
27718 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27719 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27720 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27721 center = this.imageEl.OriginHeight / 2;
27724 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27726 contextEl.translate(center, center);
27727 contextEl.rotate(this.rotate * Math.PI / 180);
27729 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27731 this.canvasEl = document.createElement("canvas");
27733 this.contextEl = this.canvasEl.getContext("2d");
27735 switch (this.rotate) {
27738 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27739 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27741 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27746 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27747 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27749 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27750 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);
27754 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27759 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27760 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27762 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27763 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);
27767 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);
27772 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27773 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27775 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27776 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27780 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);
27787 this.previewEl.appendChild(this.canvasEl);
27789 this.setCanvasPosition();
27794 if(!this.canvasLoaded){
27798 var imageCanvas = document.createElement("canvas");
27800 var imageContext = imageCanvas.getContext("2d");
27802 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27803 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27805 var center = imageCanvas.width / 2;
27807 imageContext.translate(center, center);
27809 imageContext.rotate(this.rotate * Math.PI / 180);
27811 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27813 var canvas = document.createElement("canvas");
27815 var context = canvas.getContext("2d");
27817 canvas.width = this.minWidth;
27818 canvas.height = this.minHeight;
27820 switch (this.rotate) {
27823 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27824 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27826 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27827 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27829 var targetWidth = this.minWidth - 2 * x;
27830 var targetHeight = this.minHeight - 2 * y;
27834 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27835 scale = targetWidth / width;
27838 if(x > 0 && y == 0){
27839 scale = targetHeight / height;
27842 if(x > 0 && y > 0){
27843 scale = targetWidth / width;
27845 if(width < height){
27846 scale = targetHeight / height;
27850 context.scale(scale, scale);
27852 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27853 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27855 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27856 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27858 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27863 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27864 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27866 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27867 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27869 var targetWidth = this.minWidth - 2 * x;
27870 var targetHeight = this.minHeight - 2 * y;
27874 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27875 scale = targetWidth / width;
27878 if(x > 0 && y == 0){
27879 scale = targetHeight / height;
27882 if(x > 0 && y > 0){
27883 scale = targetWidth / width;
27885 if(width < height){
27886 scale = targetHeight / height;
27890 context.scale(scale, scale);
27892 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27893 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27895 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27896 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27898 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27900 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27905 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27906 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27908 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27909 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27911 var targetWidth = this.minWidth - 2 * x;
27912 var targetHeight = this.minHeight - 2 * y;
27916 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27917 scale = targetWidth / width;
27920 if(x > 0 && y == 0){
27921 scale = targetHeight / height;
27924 if(x > 0 && y > 0){
27925 scale = targetWidth / width;
27927 if(width < height){
27928 scale = targetHeight / height;
27932 context.scale(scale, scale);
27934 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27935 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27937 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27938 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27940 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27941 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27943 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27948 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27949 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27951 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27952 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27954 var targetWidth = this.minWidth - 2 * x;
27955 var targetHeight = this.minHeight - 2 * y;
27959 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27960 scale = targetWidth / width;
27963 if(x > 0 && y == 0){
27964 scale = targetHeight / height;
27967 if(x > 0 && y > 0){
27968 scale = targetWidth / width;
27970 if(width < height){
27971 scale = targetHeight / height;
27975 context.scale(scale, scale);
27977 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27978 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27980 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27981 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27983 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27985 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27992 this.cropData = canvas.toDataURL(this.cropType);
27994 if(this.fireEvent('crop', this, this.cropData) !== false){
27995 this.process(this.file, this.cropData);
28002 setThumbBoxSize : function()
28006 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28007 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28008 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28010 this.minWidth = width;
28011 this.minHeight = height;
28013 if(this.rotate == 90 || this.rotate == 270){
28014 this.minWidth = height;
28015 this.minHeight = width;
28020 width = Math.ceil(this.minWidth * height / this.minHeight);
28022 if(this.minWidth > this.minHeight){
28024 height = Math.ceil(this.minHeight * width / this.minWidth);
28027 this.thumbEl.setStyle({
28028 width : width + 'px',
28029 height : height + 'px'
28036 setThumbBoxPosition : function()
28038 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28039 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28041 this.thumbEl.setLeft(x);
28042 this.thumbEl.setTop(y);
28046 baseRotateLevel : function()
28048 this.baseRotate = 1;
28051 typeof(this.exif) != 'undefined' &&
28052 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28053 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28055 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28058 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28062 baseScaleLevel : function()
28066 if(this.isDocument){
28068 if(this.baseRotate == 6 || this.baseRotate == 8){
28070 height = this.thumbEl.getHeight();
28071 this.baseScale = height / this.imageEl.OriginWidth;
28073 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28074 width = this.thumbEl.getWidth();
28075 this.baseScale = width / this.imageEl.OriginHeight;
28081 height = this.thumbEl.getHeight();
28082 this.baseScale = height / this.imageEl.OriginHeight;
28084 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28085 width = this.thumbEl.getWidth();
28086 this.baseScale = width / this.imageEl.OriginWidth;
28092 if(this.baseRotate == 6 || this.baseRotate == 8){
28094 width = this.thumbEl.getHeight();
28095 this.baseScale = width / this.imageEl.OriginHeight;
28097 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28098 height = this.thumbEl.getWidth();
28099 this.baseScale = height / this.imageEl.OriginHeight;
28102 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28103 height = this.thumbEl.getWidth();
28104 this.baseScale = height / this.imageEl.OriginHeight;
28106 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28107 width = this.thumbEl.getHeight();
28108 this.baseScale = width / this.imageEl.OriginWidth;
28115 width = this.thumbEl.getWidth();
28116 this.baseScale = width / this.imageEl.OriginWidth;
28118 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28119 height = this.thumbEl.getHeight();
28120 this.baseScale = height / this.imageEl.OriginHeight;
28123 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28125 height = this.thumbEl.getHeight();
28126 this.baseScale = height / this.imageEl.OriginHeight;
28128 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28129 width = this.thumbEl.getWidth();
28130 this.baseScale = width / this.imageEl.OriginWidth;
28138 getScaleLevel : function()
28140 return this.baseScale * Math.pow(1.1, this.scale);
28143 onTouchStart : function(e)
28145 if(!this.canvasLoaded){
28146 this.beforeSelectFile(e);
28150 var touches = e.browserEvent.touches;
28156 if(touches.length == 1){
28157 this.onMouseDown(e);
28161 if(touches.length != 2){
28167 for(var i = 0, finger; finger = touches[i]; i++){
28168 coords.push(finger.pageX, finger.pageY);
28171 var x = Math.pow(coords[0] - coords[2], 2);
28172 var y = Math.pow(coords[1] - coords[3], 2);
28174 this.startDistance = Math.sqrt(x + y);
28176 this.startScale = this.scale;
28178 this.pinching = true;
28179 this.dragable = false;
28183 onTouchMove : function(e)
28185 if(!this.pinching && !this.dragable){
28189 var touches = e.browserEvent.touches;
28196 this.onMouseMove(e);
28202 for(var i = 0, finger; finger = touches[i]; i++){
28203 coords.push(finger.pageX, finger.pageY);
28206 var x = Math.pow(coords[0] - coords[2], 2);
28207 var y = Math.pow(coords[1] - coords[3], 2);
28209 this.endDistance = Math.sqrt(x + y);
28211 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28213 if(!this.zoomable()){
28214 this.scale = this.startScale;
28222 onTouchEnd : function(e)
28224 this.pinching = false;
28225 this.dragable = false;
28229 process : function(file, crop)
28232 this.maskEl.mask(this.loadingText);
28235 this.xhr = new XMLHttpRequest();
28237 file.xhr = this.xhr;
28239 this.xhr.open(this.method, this.url, true);
28242 "Accept": "application/json",
28243 "Cache-Control": "no-cache",
28244 "X-Requested-With": "XMLHttpRequest"
28247 for (var headerName in headers) {
28248 var headerValue = headers[headerName];
28250 this.xhr.setRequestHeader(headerName, headerValue);
28256 this.xhr.onload = function()
28258 _this.xhrOnLoad(_this.xhr);
28261 this.xhr.onerror = function()
28263 _this.xhrOnError(_this.xhr);
28266 var formData = new FormData();
28268 formData.append('returnHTML', 'NO');
28271 formData.append('crop', crop);
28274 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28275 formData.append(this.paramName, file, file.name);
28278 if(typeof(file.filename) != 'undefined'){
28279 formData.append('filename', file.filename);
28282 if(typeof(file.mimetype) != 'undefined'){
28283 formData.append('mimetype', file.mimetype);
28286 if(this.fireEvent('arrange', this, formData) != false){
28287 this.xhr.send(formData);
28291 xhrOnLoad : function(xhr)
28294 this.maskEl.unmask();
28297 if (xhr.readyState !== 4) {
28298 this.fireEvent('exception', this, xhr);
28302 var response = Roo.decode(xhr.responseText);
28304 if(!response.success){
28305 this.fireEvent('exception', this, xhr);
28309 var response = Roo.decode(xhr.responseText);
28311 this.fireEvent('upload', this, response);
28315 xhrOnError : function()
28318 this.maskEl.unmask();
28321 Roo.log('xhr on error');
28323 var response = Roo.decode(xhr.responseText);
28329 prepare : function(file)
28332 this.maskEl.mask(this.loadingText);
28338 if(typeof(file) === 'string'){
28339 this.loadCanvas(file);
28343 if(!file || !this.urlAPI){
28348 this.cropType = file.type;
28352 if(this.fireEvent('prepare', this, this.file) != false){
28354 var reader = new FileReader();
28356 reader.onload = function (e) {
28357 if (e.target.error) {
28358 Roo.log(e.target.error);
28362 var buffer = e.target.result,
28363 dataView = new DataView(buffer),
28365 maxOffset = dataView.byteLength - 4,
28369 if (dataView.getUint16(0) === 0xffd8) {
28370 while (offset < maxOffset) {
28371 markerBytes = dataView.getUint16(offset);
28373 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28374 markerLength = dataView.getUint16(offset + 2) + 2;
28375 if (offset + markerLength > dataView.byteLength) {
28376 Roo.log('Invalid meta data: Invalid segment size.');
28380 if(markerBytes == 0xffe1){
28381 _this.parseExifData(
28388 offset += markerLength;
28398 var url = _this.urlAPI.createObjectURL(_this.file);
28400 _this.loadCanvas(url);
28405 reader.readAsArrayBuffer(this.file);
28411 parseExifData : function(dataView, offset, length)
28413 var tiffOffset = offset + 10,
28417 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28418 // No Exif data, might be XMP data instead
28422 // Check for the ASCII code for "Exif" (0x45786966):
28423 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28424 // No Exif data, might be XMP data instead
28427 if (tiffOffset + 8 > dataView.byteLength) {
28428 Roo.log('Invalid Exif data: Invalid segment size.');
28431 // Check for the two null bytes:
28432 if (dataView.getUint16(offset + 8) !== 0x0000) {
28433 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28436 // Check the byte alignment:
28437 switch (dataView.getUint16(tiffOffset)) {
28439 littleEndian = true;
28442 littleEndian = false;
28445 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28448 // Check for the TIFF tag marker (0x002A):
28449 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28450 Roo.log('Invalid Exif data: Missing TIFF marker.');
28453 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28454 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28456 this.parseExifTags(
28459 tiffOffset + dirOffset,
28464 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28469 if (dirOffset + 6 > dataView.byteLength) {
28470 Roo.log('Invalid Exif data: Invalid directory offset.');
28473 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28474 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28475 if (dirEndOffset + 4 > dataView.byteLength) {
28476 Roo.log('Invalid Exif data: Invalid directory size.');
28479 for (i = 0; i < tagsNumber; i += 1) {
28483 dirOffset + 2 + 12 * i, // tag offset
28487 // Return the offset to the next directory:
28488 return dataView.getUint32(dirEndOffset, littleEndian);
28491 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28493 var tag = dataView.getUint16(offset, littleEndian);
28495 this.exif[tag] = this.getExifValue(
28499 dataView.getUint16(offset + 2, littleEndian), // tag type
28500 dataView.getUint32(offset + 4, littleEndian), // tag length
28505 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28507 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28516 Roo.log('Invalid Exif data: Invalid tag type.');
28520 tagSize = tagType.size * length;
28521 // Determine if the value is contained in the dataOffset bytes,
28522 // or if the value at the dataOffset is a pointer to the actual data:
28523 dataOffset = tagSize > 4 ?
28524 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28525 if (dataOffset + tagSize > dataView.byteLength) {
28526 Roo.log('Invalid Exif data: Invalid data offset.');
28529 if (length === 1) {
28530 return tagType.getValue(dataView, dataOffset, littleEndian);
28533 for (i = 0; i < length; i += 1) {
28534 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28537 if (tagType.ascii) {
28539 // Concatenate the chars:
28540 for (i = 0; i < values.length; i += 1) {
28542 // Ignore the terminating NULL byte(s):
28543 if (c === '\u0000') {
28555 Roo.apply(Roo.bootstrap.UploadCropbox, {
28557 'Orientation': 0x0112
28561 1: 0, //'top-left',
28563 3: 180, //'bottom-right',
28564 // 4: 'bottom-left',
28566 6: 90, //'right-top',
28567 // 7: 'right-bottom',
28568 8: 270 //'left-bottom'
28572 // byte, 8-bit unsigned int:
28574 getValue: function (dataView, dataOffset) {
28575 return dataView.getUint8(dataOffset);
28579 // ascii, 8-bit byte:
28581 getValue: function (dataView, dataOffset) {
28582 return String.fromCharCode(dataView.getUint8(dataOffset));
28587 // short, 16 bit int:
28589 getValue: function (dataView, dataOffset, littleEndian) {
28590 return dataView.getUint16(dataOffset, littleEndian);
28594 // long, 32 bit int:
28596 getValue: function (dataView, dataOffset, littleEndian) {
28597 return dataView.getUint32(dataOffset, littleEndian);
28601 // rational = two long values, first is numerator, second is denominator:
28603 getValue: function (dataView, dataOffset, littleEndian) {
28604 return dataView.getUint32(dataOffset, littleEndian) /
28605 dataView.getUint32(dataOffset + 4, littleEndian);
28609 // slong, 32 bit signed int:
28611 getValue: function (dataView, dataOffset, littleEndian) {
28612 return dataView.getInt32(dataOffset, littleEndian);
28616 // srational, two slongs, first is numerator, second is denominator:
28618 getValue: function (dataView, dataOffset, littleEndian) {
28619 return dataView.getInt32(dataOffset, littleEndian) /
28620 dataView.getInt32(dataOffset + 4, littleEndian);
28630 cls : 'btn-group roo-upload-cropbox-rotate-left',
28631 action : 'rotate-left',
28635 cls : 'btn btn-default',
28636 html : '<i class="fa fa-undo"></i>'
28642 cls : 'btn-group roo-upload-cropbox-picture',
28643 action : 'picture',
28647 cls : 'btn btn-default',
28648 html : '<i class="fa fa-picture-o"></i>'
28654 cls : 'btn-group roo-upload-cropbox-rotate-right',
28655 action : 'rotate-right',
28659 cls : 'btn btn-default',
28660 html : '<i class="fa fa-repeat"></i>'
28668 cls : 'btn-group roo-upload-cropbox-rotate-left',
28669 action : 'rotate-left',
28673 cls : 'btn btn-default',
28674 html : '<i class="fa fa-undo"></i>'
28680 cls : 'btn-group roo-upload-cropbox-download',
28681 action : 'download',
28685 cls : 'btn btn-default',
28686 html : '<i class="fa fa-download"></i>'
28692 cls : 'btn-group roo-upload-cropbox-crop',
28697 cls : 'btn btn-default',
28698 html : '<i class="fa fa-crop"></i>'
28704 cls : 'btn-group roo-upload-cropbox-trash',
28709 cls : 'btn btn-default',
28710 html : '<i class="fa fa-trash"></i>'
28716 cls : 'btn-group roo-upload-cropbox-rotate-right',
28717 action : 'rotate-right',
28721 cls : 'btn btn-default',
28722 html : '<i class="fa fa-repeat"></i>'
28730 cls : 'btn-group roo-upload-cropbox-rotate-left',
28731 action : 'rotate-left',
28735 cls : 'btn btn-default',
28736 html : '<i class="fa fa-undo"></i>'
28742 cls : 'btn-group roo-upload-cropbox-rotate-right',
28743 action : 'rotate-right',
28747 cls : 'btn btn-default',
28748 html : '<i class="fa fa-repeat"></i>'
28761 * @class Roo.bootstrap.DocumentManager
28762 * @extends Roo.bootstrap.Component
28763 * Bootstrap DocumentManager class
28764 * @cfg {String} paramName default 'imageUpload'
28765 * @cfg {String} toolTipName default 'filename'
28766 * @cfg {String} method default POST
28767 * @cfg {String} url action url
28768 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28769 * @cfg {Boolean} multiple multiple upload default true
28770 * @cfg {Number} thumbSize default 300
28771 * @cfg {String} fieldLabel
28772 * @cfg {Number} labelWidth default 4
28773 * @cfg {String} labelAlign (left|top) default left
28774 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28775 * @cfg {Number} labellg set the width of label (1-12)
28776 * @cfg {Number} labelmd set the width of label (1-12)
28777 * @cfg {Number} labelsm set the width of label (1-12)
28778 * @cfg {Number} labelxs set the width of label (1-12)
28781 * Create a new DocumentManager
28782 * @param {Object} config The config object
28785 Roo.bootstrap.DocumentManager = function(config){
28786 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28789 this.delegates = [];
28794 * Fire when initial the DocumentManager
28795 * @param {Roo.bootstrap.DocumentManager} this
28800 * inspect selected file
28801 * @param {Roo.bootstrap.DocumentManager} this
28802 * @param {File} file
28807 * Fire when xhr load exception
28808 * @param {Roo.bootstrap.DocumentManager} this
28809 * @param {XMLHttpRequest} xhr
28811 "exception" : true,
28813 * @event afterupload
28814 * Fire when xhr load exception
28815 * @param {Roo.bootstrap.DocumentManager} this
28816 * @param {XMLHttpRequest} xhr
28818 "afterupload" : true,
28821 * prepare the form data
28822 * @param {Roo.bootstrap.DocumentManager} this
28823 * @param {Object} formData
28828 * Fire when remove the file
28829 * @param {Roo.bootstrap.DocumentManager} this
28830 * @param {Object} file
28835 * Fire after refresh the file
28836 * @param {Roo.bootstrap.DocumentManager} this
28841 * Fire after click the image
28842 * @param {Roo.bootstrap.DocumentManager} this
28843 * @param {Object} file
28848 * Fire when upload a image and editable set to true
28849 * @param {Roo.bootstrap.DocumentManager} this
28850 * @param {Object} file
28854 * @event beforeselectfile
28855 * Fire before select file
28856 * @param {Roo.bootstrap.DocumentManager} this
28858 "beforeselectfile" : true,
28861 * Fire before process file
28862 * @param {Roo.bootstrap.DocumentManager} this
28863 * @param {Object} file
28867 * @event previewrendered
28868 * Fire when preview rendered
28869 * @param {Roo.bootstrap.DocumentManager} this
28870 * @param {Object} file
28872 "previewrendered" : true,
28875 "previewResize" : true
28880 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28889 paramName : 'imageUpload',
28890 toolTipName : 'filename',
28893 labelAlign : 'left',
28903 getAutoCreate : function()
28905 var managerWidget = {
28907 cls : 'roo-document-manager',
28911 cls : 'roo-document-manager-selector',
28916 cls : 'roo-document-manager-uploader',
28920 cls : 'roo-document-manager-upload-btn',
28921 html : '<i class="fa fa-plus"></i>'
28932 cls : 'column col-md-12',
28937 if(this.fieldLabel.length){
28942 cls : 'column col-md-12',
28943 html : this.fieldLabel
28947 cls : 'column col-md-12',
28952 if(this.labelAlign == 'left'){
28957 html : this.fieldLabel
28966 if(this.labelWidth > 12){
28967 content[0].style = "width: " + this.labelWidth + 'px';
28970 if(this.labelWidth < 13 && this.labelmd == 0){
28971 this.labelmd = this.labelWidth;
28974 if(this.labellg > 0){
28975 content[0].cls += ' col-lg-' + this.labellg;
28976 content[1].cls += ' col-lg-' + (12 - this.labellg);
28979 if(this.labelmd > 0){
28980 content[0].cls += ' col-md-' + this.labelmd;
28981 content[1].cls += ' col-md-' + (12 - this.labelmd);
28984 if(this.labelsm > 0){
28985 content[0].cls += ' col-sm-' + this.labelsm;
28986 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28989 if(this.labelxs > 0){
28990 content[0].cls += ' col-xs-' + this.labelxs;
28991 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28999 cls : 'row clearfix',
29007 initEvents : function()
29009 this.managerEl = this.el.select('.roo-document-manager', true).first();
29010 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29012 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29013 this.selectorEl.hide();
29016 this.selectorEl.attr('multiple', 'multiple');
29019 this.selectorEl.on('change', this.onFileSelected, this);
29021 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29022 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29024 this.uploader.on('click', this.onUploaderClick, this);
29026 this.renderProgressDialog();
29030 window.addEventListener("resize", function() { _this.refresh(); } );
29032 this.fireEvent('initial', this);
29035 renderProgressDialog : function()
29039 this.progressDialog = new Roo.bootstrap.Modal({
29040 cls : 'roo-document-manager-progress-dialog',
29041 allow_close : false,
29051 btnclick : function() {
29052 _this.uploadCancel();
29058 this.progressDialog.render(Roo.get(document.body));
29060 this.progress = new Roo.bootstrap.Progress({
29061 cls : 'roo-document-manager-progress',
29066 this.progress.render(this.progressDialog.getChildContainer());
29068 this.progressBar = new Roo.bootstrap.ProgressBar({
29069 cls : 'roo-document-manager-progress-bar',
29072 aria_valuemax : 12,
29076 this.progressBar.render(this.progress.getChildContainer());
29079 onUploaderClick : function(e)
29081 e.preventDefault();
29083 if(this.fireEvent('beforeselectfile', this) != false){
29084 this.selectorEl.dom.click();
29089 onFileSelected : function(e)
29091 e.preventDefault();
29093 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29097 Roo.each(this.selectorEl.dom.files, function(file){
29098 if(this.fireEvent('inspect', this, file) != false){
29099 this.files.push(file);
29109 this.selectorEl.dom.value = '';
29111 if(!this.files || !this.files.length){
29115 if(this.boxes > 0 && this.files.length > this.boxes){
29116 this.files = this.files.slice(0, this.boxes);
29119 this.uploader.show();
29121 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29122 this.uploader.hide();
29131 Roo.each(this.files, function(file){
29133 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29134 var f = this.renderPreview(file);
29139 if(file.type.indexOf('image') != -1){
29140 this.delegates.push(
29142 _this.process(file);
29143 }).createDelegate(this)
29151 _this.process(file);
29152 }).createDelegate(this)
29157 this.files = files;
29159 this.delegates = this.delegates.concat(docs);
29161 if(!this.delegates.length){
29166 this.progressBar.aria_valuemax = this.delegates.length;
29173 arrange : function()
29175 if(!this.delegates.length){
29176 this.progressDialog.hide();
29181 var delegate = this.delegates.shift();
29183 this.progressDialog.show();
29185 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29187 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29192 refresh : function()
29194 this.uploader.show();
29196 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29197 this.uploader.hide();
29200 Roo.isTouch ? this.closable(false) : this.closable(true);
29202 this.fireEvent('refresh', this);
29205 onRemove : function(e, el, o)
29207 e.preventDefault();
29209 this.fireEvent('remove', this, o);
29213 remove : function(o)
29217 Roo.each(this.files, function(file){
29218 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29227 this.files = files;
29234 Roo.each(this.files, function(file){
29239 file.target.remove();
29248 onClick : function(e, el, o)
29250 e.preventDefault();
29252 this.fireEvent('click', this, o);
29256 closable : function(closable)
29258 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29260 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29272 xhrOnLoad : function(xhr)
29274 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29278 if (xhr.readyState !== 4) {
29280 this.fireEvent('exception', this, xhr);
29284 var response = Roo.decode(xhr.responseText);
29286 if(!response.success){
29288 this.fireEvent('exception', this, xhr);
29292 var file = this.renderPreview(response.data);
29294 this.files.push(file);
29298 this.fireEvent('afterupload', this, xhr);
29302 xhrOnError : function(xhr)
29304 Roo.log('xhr on error');
29306 var response = Roo.decode(xhr.responseText);
29313 process : function(file)
29315 if(this.fireEvent('process', this, file) !== false){
29316 if(this.editable && file.type.indexOf('image') != -1){
29317 this.fireEvent('edit', this, file);
29321 this.uploadStart(file, false);
29328 uploadStart : function(file, crop)
29330 this.xhr = new XMLHttpRequest();
29332 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29337 file.xhr = this.xhr;
29339 this.managerEl.createChild({
29341 cls : 'roo-document-manager-loading',
29345 tooltip : file.name,
29346 cls : 'roo-document-manager-thumb',
29347 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29353 this.xhr.open(this.method, this.url, true);
29356 "Accept": "application/json",
29357 "Cache-Control": "no-cache",
29358 "X-Requested-With": "XMLHttpRequest"
29361 for (var headerName in headers) {
29362 var headerValue = headers[headerName];
29364 this.xhr.setRequestHeader(headerName, headerValue);
29370 this.xhr.onload = function()
29372 _this.xhrOnLoad(_this.xhr);
29375 this.xhr.onerror = function()
29377 _this.xhrOnError(_this.xhr);
29380 var formData = new FormData();
29382 formData.append('returnHTML', 'NO');
29385 formData.append('crop', crop);
29388 formData.append(this.paramName, file, file.name);
29395 if(this.fireEvent('prepare', this, formData, options) != false){
29397 if(options.manually){
29401 this.xhr.send(formData);
29405 this.uploadCancel();
29408 uploadCancel : function()
29414 this.delegates = [];
29416 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29423 renderPreview : function(file)
29425 if(typeof(file.target) != 'undefined' && file.target){
29429 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29431 var previewEl = this.managerEl.createChild({
29433 cls : 'roo-document-manager-preview',
29437 tooltip : file[this.toolTipName],
29438 cls : 'roo-document-manager-thumb',
29439 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29444 html : '<i class="fa fa-times-circle"></i>'
29449 var close = previewEl.select('button.close', true).first();
29451 close.on('click', this.onRemove, this, file);
29453 file.target = previewEl;
29455 var image = previewEl.select('img', true).first();
29459 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29461 image.on('click', this.onClick, this, file);
29463 this.fireEvent('previewrendered', this, file);
29469 onPreviewLoad : function(file, image)
29471 if(typeof(file.target) == 'undefined' || !file.target){
29475 var width = image.dom.naturalWidth || image.dom.width;
29476 var height = image.dom.naturalHeight || image.dom.height;
29478 if(!this.previewResize) {
29482 if(width > height){
29483 file.target.addClass('wide');
29487 file.target.addClass('tall');
29492 uploadFromSource : function(file, crop)
29494 this.xhr = new XMLHttpRequest();
29496 this.managerEl.createChild({
29498 cls : 'roo-document-manager-loading',
29502 tooltip : file.name,
29503 cls : 'roo-document-manager-thumb',
29504 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29510 this.xhr.open(this.method, this.url, true);
29513 "Accept": "application/json",
29514 "Cache-Control": "no-cache",
29515 "X-Requested-With": "XMLHttpRequest"
29518 for (var headerName in headers) {
29519 var headerValue = headers[headerName];
29521 this.xhr.setRequestHeader(headerName, headerValue);
29527 this.xhr.onload = function()
29529 _this.xhrOnLoad(_this.xhr);
29532 this.xhr.onerror = function()
29534 _this.xhrOnError(_this.xhr);
29537 var formData = new FormData();
29539 formData.append('returnHTML', 'NO');
29541 formData.append('crop', crop);
29543 if(typeof(file.filename) != 'undefined'){
29544 formData.append('filename', file.filename);
29547 if(typeof(file.mimetype) != 'undefined'){
29548 formData.append('mimetype', file.mimetype);
29553 if(this.fireEvent('prepare', this, formData) != false){
29554 this.xhr.send(formData);
29564 * @class Roo.bootstrap.DocumentViewer
29565 * @extends Roo.bootstrap.Component
29566 * Bootstrap DocumentViewer class
29567 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29568 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29571 * Create a new DocumentViewer
29572 * @param {Object} config The config object
29575 Roo.bootstrap.DocumentViewer = function(config){
29576 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29581 * Fire after initEvent
29582 * @param {Roo.bootstrap.DocumentViewer} this
29588 * @param {Roo.bootstrap.DocumentViewer} this
29593 * Fire after download button
29594 * @param {Roo.bootstrap.DocumentViewer} this
29599 * Fire after trash button
29600 * @param {Roo.bootstrap.DocumentViewer} this
29607 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29609 showDownload : true,
29613 getAutoCreate : function()
29617 cls : 'roo-document-viewer',
29621 cls : 'roo-document-viewer-body',
29625 cls : 'roo-document-viewer-thumb',
29629 cls : 'roo-document-viewer-image'
29637 cls : 'roo-document-viewer-footer',
29640 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29644 cls : 'btn-group roo-document-viewer-download',
29648 cls : 'btn btn-default',
29649 html : '<i class="fa fa-download"></i>'
29655 cls : 'btn-group roo-document-viewer-trash',
29659 cls : 'btn btn-default',
29660 html : '<i class="fa fa-trash"></i>'
29673 initEvents : function()
29675 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29676 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29678 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29679 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29681 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29682 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29684 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29685 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29687 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29688 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29690 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29691 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29693 this.bodyEl.on('click', this.onClick, this);
29694 this.downloadBtn.on('click', this.onDownload, this);
29695 this.trashBtn.on('click', this.onTrash, this);
29697 this.downloadBtn.hide();
29698 this.trashBtn.hide();
29700 if(this.showDownload){
29701 this.downloadBtn.show();
29704 if(this.showTrash){
29705 this.trashBtn.show();
29708 if(!this.showDownload && !this.showTrash) {
29709 this.footerEl.hide();
29714 initial : function()
29716 this.fireEvent('initial', this);
29720 onClick : function(e)
29722 e.preventDefault();
29724 this.fireEvent('click', this);
29727 onDownload : function(e)
29729 e.preventDefault();
29731 this.fireEvent('download', this);
29734 onTrash : function(e)
29736 e.preventDefault();
29738 this.fireEvent('trash', this);
29750 * @class Roo.bootstrap.NavProgressBar
29751 * @extends Roo.bootstrap.Component
29752 * Bootstrap NavProgressBar class
29755 * Create a new nav progress bar
29756 * @param {Object} config The config object
29759 Roo.bootstrap.NavProgressBar = function(config){
29760 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29762 this.bullets = this.bullets || [];
29764 // Roo.bootstrap.NavProgressBar.register(this);
29768 * Fires when the active item changes
29769 * @param {Roo.bootstrap.NavProgressBar} this
29770 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29771 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29778 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29783 getAutoCreate : function()
29785 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29789 cls : 'roo-navigation-bar-group',
29793 cls : 'roo-navigation-top-bar'
29797 cls : 'roo-navigation-bullets-bar',
29801 cls : 'roo-navigation-bar'
29808 cls : 'roo-navigation-bottom-bar'
29818 initEvents: function()
29823 onRender : function(ct, position)
29825 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29827 if(this.bullets.length){
29828 Roo.each(this.bullets, function(b){
29837 addItem : function(cfg)
29839 var item = new Roo.bootstrap.NavProgressItem(cfg);
29841 item.parentId = this.id;
29842 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29845 var top = new Roo.bootstrap.Element({
29847 cls : 'roo-navigation-bar-text'
29850 var bottom = new Roo.bootstrap.Element({
29852 cls : 'roo-navigation-bar-text'
29855 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29856 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29858 var topText = new Roo.bootstrap.Element({
29860 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29863 var bottomText = new Roo.bootstrap.Element({
29865 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29868 topText.onRender(top.el, null);
29869 bottomText.onRender(bottom.el, null);
29872 item.bottomEl = bottom;
29875 this.barItems.push(item);
29880 getActive : function()
29882 var active = false;
29884 Roo.each(this.barItems, function(v){
29886 if (!v.isActive()) {
29898 setActiveItem : function(item)
29902 Roo.each(this.barItems, function(v){
29903 if (v.rid == item.rid) {
29907 if (v.isActive()) {
29908 v.setActive(false);
29913 item.setActive(true);
29915 this.fireEvent('changed', this, item, prev);
29918 getBarItem: function(rid)
29922 Roo.each(this.barItems, function(e) {
29923 if (e.rid != rid) {
29934 indexOfItem : function(item)
29938 Roo.each(this.barItems, function(v, i){
29940 if (v.rid != item.rid) {
29951 setActiveNext : function()
29953 var i = this.indexOfItem(this.getActive());
29955 if (i > this.barItems.length) {
29959 this.setActiveItem(this.barItems[i+1]);
29962 setActivePrev : function()
29964 var i = this.indexOfItem(this.getActive());
29970 this.setActiveItem(this.barItems[i-1]);
29973 format : function()
29975 if(!this.barItems.length){
29979 var width = 100 / this.barItems.length;
29981 Roo.each(this.barItems, function(i){
29982 i.el.setStyle('width', width + '%');
29983 i.topEl.el.setStyle('width', width + '%');
29984 i.bottomEl.el.setStyle('width', width + '%');
29993 * Nav Progress Item
29998 * @class Roo.bootstrap.NavProgressItem
29999 * @extends Roo.bootstrap.Component
30000 * Bootstrap NavProgressItem class
30001 * @cfg {String} rid the reference id
30002 * @cfg {Boolean} active (true|false) Is item active default false
30003 * @cfg {Boolean} disabled (true|false) Is item active default false
30004 * @cfg {String} html
30005 * @cfg {String} position (top|bottom) text position default bottom
30006 * @cfg {String} icon show icon instead of number
30009 * Create a new NavProgressItem
30010 * @param {Object} config The config object
30012 Roo.bootstrap.NavProgressItem = function(config){
30013 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30018 * The raw click event for the entire grid.
30019 * @param {Roo.bootstrap.NavProgressItem} this
30020 * @param {Roo.EventObject} e
30027 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30033 position : 'bottom',
30036 getAutoCreate : function()
30038 var iconCls = 'roo-navigation-bar-item-icon';
30040 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30044 cls: 'roo-navigation-bar-item',
30054 cfg.cls += ' active';
30057 cfg.cls += ' disabled';
30063 disable : function()
30065 this.setDisabled(true);
30068 enable : function()
30070 this.setDisabled(false);
30073 initEvents: function()
30075 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30077 this.iconEl.on('click', this.onClick, this);
30080 onClick : function(e)
30082 e.preventDefault();
30088 if(this.fireEvent('click', this, e) === false){
30092 this.parent().setActiveItem(this);
30095 isActive: function ()
30097 return this.active;
30100 setActive : function(state)
30102 if(this.active == state){
30106 this.active = state;
30109 this.el.addClass('active');
30113 this.el.removeClass('active');
30118 setDisabled : function(state)
30120 if(this.disabled == state){
30124 this.disabled = state;
30127 this.el.addClass('disabled');
30131 this.el.removeClass('disabled');
30134 tooltipEl : function()
30136 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30149 * @class Roo.bootstrap.FieldLabel
30150 * @extends Roo.bootstrap.Component
30151 * Bootstrap FieldLabel class
30152 * @cfg {String} html contents of the element
30153 * @cfg {String} tag tag of the element default label
30154 * @cfg {String} cls class of the element
30155 * @cfg {String} target label target
30156 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30157 * @cfg {String} invalidClass default "text-warning"
30158 * @cfg {String} validClass default "text-success"
30159 * @cfg {String} iconTooltip default "This field is required"
30160 * @cfg {String} indicatorpos (left|right) default left
30163 * Create a new FieldLabel
30164 * @param {Object} config The config object
30167 Roo.bootstrap.FieldLabel = function(config){
30168 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30173 * Fires after the field has been marked as invalid.
30174 * @param {Roo.form.FieldLabel} this
30175 * @param {String} msg The validation message
30180 * Fires after the field has been validated with no errors.
30181 * @param {Roo.form.FieldLabel} this
30187 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30194 invalidClass : 'has-warning',
30195 validClass : 'has-success',
30196 iconTooltip : 'This field is required',
30197 indicatorpos : 'left',
30199 getAutoCreate : function(){
30202 if (!this.allowBlank) {
30208 cls : 'roo-bootstrap-field-label ' + this.cls,
30213 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30214 tooltip : this.iconTooltip
30223 if(this.indicatorpos == 'right'){
30226 cls : 'roo-bootstrap-field-label ' + this.cls,
30235 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30236 tooltip : this.iconTooltip
30245 initEvents: function()
30247 Roo.bootstrap.Element.superclass.initEvents.call(this);
30249 this.indicator = this.indicatorEl();
30251 if(this.indicator){
30252 this.indicator.removeClass('visible');
30253 this.indicator.addClass('invisible');
30256 Roo.bootstrap.FieldLabel.register(this);
30259 indicatorEl : function()
30261 var indicator = this.el.select('i.roo-required-indicator',true).first();
30272 * Mark this field as valid
30274 markValid : function()
30276 if(this.indicator){
30277 this.indicator.removeClass('visible');
30278 this.indicator.addClass('invisible');
30281 this.el.removeClass(this.invalidClass);
30283 this.el.addClass(this.validClass);
30285 this.fireEvent('valid', this);
30289 * Mark this field as invalid
30290 * @param {String} msg The validation message
30292 markInvalid : function(msg)
30294 if(this.indicator){
30295 this.indicator.removeClass('invisible');
30296 this.indicator.addClass('visible');
30299 this.el.removeClass(this.validClass);
30301 this.el.addClass(this.invalidClass);
30303 this.fireEvent('invalid', this, msg);
30309 Roo.apply(Roo.bootstrap.FieldLabel, {
30314 * register a FieldLabel Group
30315 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30317 register : function(label)
30319 if(this.groups.hasOwnProperty(label.target)){
30323 this.groups[label.target] = label;
30327 * fetch a FieldLabel Group based on the target
30328 * @param {string} target
30329 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30331 get: function(target) {
30332 if (typeof(this.groups[target]) == 'undefined') {
30336 return this.groups[target] ;
30345 * page DateSplitField.
30351 * @class Roo.bootstrap.DateSplitField
30352 * @extends Roo.bootstrap.Component
30353 * Bootstrap DateSplitField class
30354 * @cfg {string} fieldLabel - the label associated
30355 * @cfg {Number} labelWidth set the width of label (0-12)
30356 * @cfg {String} labelAlign (top|left)
30357 * @cfg {Boolean} dayAllowBlank (true|false) default false
30358 * @cfg {Boolean} monthAllowBlank (true|false) default false
30359 * @cfg {Boolean} yearAllowBlank (true|false) default false
30360 * @cfg {string} dayPlaceholder
30361 * @cfg {string} monthPlaceholder
30362 * @cfg {string} yearPlaceholder
30363 * @cfg {string} dayFormat default 'd'
30364 * @cfg {string} monthFormat default 'm'
30365 * @cfg {string} yearFormat default 'Y'
30366 * @cfg {Number} labellg set the width of label (1-12)
30367 * @cfg {Number} labelmd set the width of label (1-12)
30368 * @cfg {Number} labelsm set the width of label (1-12)
30369 * @cfg {Number} labelxs set the width of label (1-12)
30373 * Create a new DateSplitField
30374 * @param {Object} config The config object
30377 Roo.bootstrap.DateSplitField = function(config){
30378 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30384 * getting the data of years
30385 * @param {Roo.bootstrap.DateSplitField} this
30386 * @param {Object} years
30391 * getting the data of days
30392 * @param {Roo.bootstrap.DateSplitField} this
30393 * @param {Object} days
30398 * Fires after the field has been marked as invalid.
30399 * @param {Roo.form.Field} this
30400 * @param {String} msg The validation message
30405 * Fires after the field has been validated with no errors.
30406 * @param {Roo.form.Field} this
30412 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30415 labelAlign : 'top',
30417 dayAllowBlank : false,
30418 monthAllowBlank : false,
30419 yearAllowBlank : false,
30420 dayPlaceholder : '',
30421 monthPlaceholder : '',
30422 yearPlaceholder : '',
30426 isFormField : true,
30432 getAutoCreate : function()
30436 cls : 'row roo-date-split-field-group',
30441 cls : 'form-hidden-field roo-date-split-field-group-value',
30447 var labelCls = 'col-md-12';
30448 var contentCls = 'col-md-4';
30450 if(this.fieldLabel){
30454 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30458 html : this.fieldLabel
30463 if(this.labelAlign == 'left'){
30465 if(this.labelWidth > 12){
30466 label.style = "width: " + this.labelWidth + 'px';
30469 if(this.labelWidth < 13 && this.labelmd == 0){
30470 this.labelmd = this.labelWidth;
30473 if(this.labellg > 0){
30474 labelCls = ' col-lg-' + this.labellg;
30475 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30478 if(this.labelmd > 0){
30479 labelCls = ' col-md-' + this.labelmd;
30480 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30483 if(this.labelsm > 0){
30484 labelCls = ' col-sm-' + this.labelsm;
30485 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30488 if(this.labelxs > 0){
30489 labelCls = ' col-xs-' + this.labelxs;
30490 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30494 label.cls += ' ' + labelCls;
30496 cfg.cn.push(label);
30499 Roo.each(['day', 'month', 'year'], function(t){
30502 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30509 inputEl: function ()
30511 return this.el.select('.roo-date-split-field-group-value', true).first();
30514 onRender : function(ct, position)
30518 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30520 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30522 this.dayField = new Roo.bootstrap.ComboBox({
30523 allowBlank : this.dayAllowBlank,
30524 alwaysQuery : true,
30525 displayField : 'value',
30528 forceSelection : true,
30530 placeholder : this.dayPlaceholder,
30531 selectOnFocus : true,
30532 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30533 triggerAction : 'all',
30535 valueField : 'value',
30536 store : new Roo.data.SimpleStore({
30537 data : (function() {
30539 _this.fireEvent('days', _this, days);
30542 fields : [ 'value' ]
30545 select : function (_self, record, index)
30547 _this.setValue(_this.getValue());
30552 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30554 this.monthField = new Roo.bootstrap.MonthField({
30555 after : '<i class=\"fa fa-calendar\"></i>',
30556 allowBlank : this.monthAllowBlank,
30557 placeholder : this.monthPlaceholder,
30560 render : function (_self)
30562 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30563 e.preventDefault();
30567 select : function (_self, oldvalue, newvalue)
30569 _this.setValue(_this.getValue());
30574 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30576 this.yearField = new Roo.bootstrap.ComboBox({
30577 allowBlank : this.yearAllowBlank,
30578 alwaysQuery : true,
30579 displayField : 'value',
30582 forceSelection : true,
30584 placeholder : this.yearPlaceholder,
30585 selectOnFocus : true,
30586 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30587 triggerAction : 'all',
30589 valueField : 'value',
30590 store : new Roo.data.SimpleStore({
30591 data : (function() {
30593 _this.fireEvent('years', _this, years);
30596 fields : [ 'value' ]
30599 select : function (_self, record, index)
30601 _this.setValue(_this.getValue());
30606 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30609 setValue : function(v, format)
30611 this.inputEl.dom.value = v;
30613 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30615 var d = Date.parseDate(v, f);
30622 this.setDay(d.format(this.dayFormat));
30623 this.setMonth(d.format(this.monthFormat));
30624 this.setYear(d.format(this.yearFormat));
30631 setDay : function(v)
30633 this.dayField.setValue(v);
30634 this.inputEl.dom.value = this.getValue();
30639 setMonth : function(v)
30641 this.monthField.setValue(v, true);
30642 this.inputEl.dom.value = this.getValue();
30647 setYear : function(v)
30649 this.yearField.setValue(v);
30650 this.inputEl.dom.value = this.getValue();
30655 getDay : function()
30657 return this.dayField.getValue();
30660 getMonth : function()
30662 return this.monthField.getValue();
30665 getYear : function()
30667 return this.yearField.getValue();
30670 getValue : function()
30672 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30674 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30684 this.inputEl.dom.value = '';
30689 validate : function()
30691 var d = this.dayField.validate();
30692 var m = this.monthField.validate();
30693 var y = this.yearField.validate();
30698 (!this.dayAllowBlank && !d) ||
30699 (!this.monthAllowBlank && !m) ||
30700 (!this.yearAllowBlank && !y)
30705 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30714 this.markInvalid();
30719 markValid : function()
30722 var label = this.el.select('label', true).first();
30723 var icon = this.el.select('i.fa-star', true).first();
30729 this.fireEvent('valid', this);
30733 * Mark this field as invalid
30734 * @param {String} msg The validation message
30736 markInvalid : function(msg)
30739 var label = this.el.select('label', true).first();
30740 var icon = this.el.select('i.fa-star', true).first();
30742 if(label && !icon){
30743 this.el.select('.roo-date-split-field-label', true).createChild({
30745 cls : 'text-danger fa fa-lg fa-star',
30746 tooltip : 'This field is required',
30747 style : 'margin-right:5px;'
30751 this.fireEvent('invalid', this, msg);
30754 clearInvalid : function()
30756 var label = this.el.select('label', true).first();
30757 var icon = this.el.select('i.fa-star', true).first();
30763 this.fireEvent('valid', this);
30766 getName: function()
30776 * http://masonry.desandro.com
30778 * The idea is to render all the bricks based on vertical width...
30780 * The original code extends 'outlayer' - we might need to use that....
30786 * @class Roo.bootstrap.LayoutMasonry
30787 * @extends Roo.bootstrap.Component
30788 * Bootstrap Layout Masonry class
30791 * Create a new Element
30792 * @param {Object} config The config object
30795 Roo.bootstrap.LayoutMasonry = function(config){
30797 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30801 Roo.bootstrap.LayoutMasonry.register(this);
30807 * Fire after layout the items
30808 * @param {Roo.bootstrap.LayoutMasonry} this
30809 * @param {Roo.EventObject} e
30816 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30819 * @cfg {Boolean} isLayoutInstant = no animation?
30821 isLayoutInstant : false, // needed?
30824 * @cfg {Number} boxWidth width of the columns
30829 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30834 * @cfg {Number} padWidth padding below box..
30839 * @cfg {Number} gutter gutter width..
30844 * @cfg {Number} maxCols maximum number of columns
30850 * @cfg {Boolean} isAutoInitial defalut true
30852 isAutoInitial : true,
30857 * @cfg {Boolean} isHorizontal defalut false
30859 isHorizontal : false,
30861 currentSize : null,
30867 bricks: null, //CompositeElement
30871 _isLayoutInited : false,
30873 // isAlternative : false, // only use for vertical layout...
30876 * @cfg {Number} alternativePadWidth padding below box..
30878 alternativePadWidth : 50,
30880 selectedBrick : [],
30882 getAutoCreate : function(){
30884 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30888 cls: 'blog-masonary-wrapper ' + this.cls,
30890 cls : 'mas-boxes masonary'
30897 getChildContainer: function( )
30899 if (this.boxesEl) {
30900 return this.boxesEl;
30903 this.boxesEl = this.el.select('.mas-boxes').first();
30905 return this.boxesEl;
30909 initEvents : function()
30913 if(this.isAutoInitial){
30914 Roo.log('hook children rendered');
30915 this.on('childrenrendered', function() {
30916 Roo.log('children rendered');
30922 initial : function()
30924 this.selectedBrick = [];
30926 this.currentSize = this.el.getBox(true);
30928 Roo.EventManager.onWindowResize(this.resize, this);
30930 if(!this.isAutoInitial){
30938 //this.layout.defer(500,this);
30942 resize : function()
30944 var cs = this.el.getBox(true);
30947 this.currentSize.width == cs.width &&
30948 this.currentSize.x == cs.x &&
30949 this.currentSize.height == cs.height &&
30950 this.currentSize.y == cs.y
30952 Roo.log("no change in with or X or Y");
30956 this.currentSize = cs;
30962 layout : function()
30964 this._resetLayout();
30966 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30968 this.layoutItems( isInstant );
30970 this._isLayoutInited = true;
30972 this.fireEvent('layout', this);
30976 _resetLayout : function()
30978 if(this.isHorizontal){
30979 this.horizontalMeasureColumns();
30983 this.verticalMeasureColumns();
30987 verticalMeasureColumns : function()
30989 this.getContainerWidth();
30991 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30992 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30996 var boxWidth = this.boxWidth + this.padWidth;
30998 if(this.containerWidth < this.boxWidth){
30999 boxWidth = this.containerWidth
31002 var containerWidth = this.containerWidth;
31004 var cols = Math.floor(containerWidth / boxWidth);
31006 this.cols = Math.max( cols, 1 );
31008 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31010 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31012 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31014 this.colWidth = boxWidth + avail - this.padWidth;
31016 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31017 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31020 horizontalMeasureColumns : function()
31022 this.getContainerWidth();
31024 var boxWidth = this.boxWidth;
31026 if(this.containerWidth < boxWidth){
31027 boxWidth = this.containerWidth;
31030 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31032 this.el.setHeight(boxWidth);
31036 getContainerWidth : function()
31038 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31041 layoutItems : function( isInstant )
31043 Roo.log(this.bricks);
31045 var items = Roo.apply([], this.bricks);
31047 if(this.isHorizontal){
31048 this._horizontalLayoutItems( items , isInstant );
31052 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31053 // this._verticalAlternativeLayoutItems( items , isInstant );
31057 this._verticalLayoutItems( items , isInstant );
31061 _verticalLayoutItems : function ( items , isInstant)
31063 if ( !items || !items.length ) {
31068 ['xs', 'xs', 'xs', 'tall'],
31069 ['xs', 'xs', 'tall'],
31070 ['xs', 'xs', 'sm'],
31071 ['xs', 'xs', 'xs'],
31077 ['sm', 'xs', 'xs'],
31081 ['tall', 'xs', 'xs', 'xs'],
31082 ['tall', 'xs', 'xs'],
31094 Roo.each(items, function(item, k){
31096 switch (item.size) {
31097 // these layouts take up a full box,
31108 boxes.push([item]);
31131 var filterPattern = function(box, length)
31139 var pattern = box.slice(0, length);
31143 Roo.each(pattern, function(i){
31144 format.push(i.size);
31147 Roo.each(standard, function(s){
31149 if(String(s) != String(format)){
31158 if(!match && length == 1){
31163 filterPattern(box, length - 1);
31167 queue.push(pattern);
31169 box = box.slice(length, box.length);
31171 filterPattern(box, 4);
31177 Roo.each(boxes, function(box, k){
31183 if(box.length == 1){
31188 filterPattern(box, 4);
31192 this._processVerticalLayoutQueue( queue, isInstant );
31196 // _verticalAlternativeLayoutItems : function( items , isInstant )
31198 // if ( !items || !items.length ) {
31202 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31206 _horizontalLayoutItems : function ( items , isInstant)
31208 if ( !items || !items.length || items.length < 3) {
31214 var eItems = items.slice(0, 3);
31216 items = items.slice(3, items.length);
31219 ['xs', 'xs', 'xs', 'wide'],
31220 ['xs', 'xs', 'wide'],
31221 ['xs', 'xs', 'sm'],
31222 ['xs', 'xs', 'xs'],
31228 ['sm', 'xs', 'xs'],
31232 ['wide', 'xs', 'xs', 'xs'],
31233 ['wide', 'xs', 'xs'],
31246 Roo.each(items, function(item, k){
31248 switch (item.size) {
31259 boxes.push([item]);
31283 var filterPattern = function(box, length)
31291 var pattern = box.slice(0, length);
31295 Roo.each(pattern, function(i){
31296 format.push(i.size);
31299 Roo.each(standard, function(s){
31301 if(String(s) != String(format)){
31310 if(!match && length == 1){
31315 filterPattern(box, length - 1);
31319 queue.push(pattern);
31321 box = box.slice(length, box.length);
31323 filterPattern(box, 4);
31329 Roo.each(boxes, function(box, k){
31335 if(box.length == 1){
31340 filterPattern(box, 4);
31347 var pos = this.el.getBox(true);
31351 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31353 var hit_end = false;
31355 Roo.each(queue, function(box){
31359 Roo.each(box, function(b){
31361 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31371 Roo.each(box, function(b){
31373 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31376 mx = Math.max(mx, b.x);
31380 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31384 Roo.each(box, function(b){
31386 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31400 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31403 /** Sets position of item in DOM
31404 * @param {Element} item
31405 * @param {Number} x - horizontal position
31406 * @param {Number} y - vertical position
31407 * @param {Boolean} isInstant - disables transitions
31409 _processVerticalLayoutQueue : function( queue, isInstant )
31411 var pos = this.el.getBox(true);
31416 for (var i = 0; i < this.cols; i++){
31420 Roo.each(queue, function(box, k){
31422 var col = k % this.cols;
31424 Roo.each(box, function(b,kk){
31426 b.el.position('absolute');
31428 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31429 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31431 if(b.size == 'md-left' || b.size == 'md-right'){
31432 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31433 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31436 b.el.setWidth(width);
31437 b.el.setHeight(height);
31439 b.el.select('iframe',true).setSize(width,height);
31443 for (var i = 0; i < this.cols; i++){
31445 if(maxY[i] < maxY[col]){
31450 col = Math.min(col, i);
31454 x = pos.x + col * (this.colWidth + this.padWidth);
31458 var positions = [];
31460 switch (box.length){
31462 positions = this.getVerticalOneBoxColPositions(x, y, box);
31465 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31468 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31471 positions = this.getVerticalFourBoxColPositions(x, y, box);
31477 Roo.each(box, function(b,kk){
31479 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31481 var sz = b.el.getSize();
31483 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31491 for (var i = 0; i < this.cols; i++){
31492 mY = Math.max(mY, maxY[i]);
31495 this.el.setHeight(mY - pos.y);
31499 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31501 // var pos = this.el.getBox(true);
31504 // var maxX = pos.right;
31506 // var maxHeight = 0;
31508 // Roo.each(items, function(item, k){
31512 // item.el.position('absolute');
31514 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31516 // item.el.setWidth(width);
31518 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31520 // item.el.setHeight(height);
31523 // item.el.setXY([x, y], isInstant ? false : true);
31525 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31528 // y = y + height + this.alternativePadWidth;
31530 // maxHeight = maxHeight + height + this.alternativePadWidth;
31534 // this.el.setHeight(maxHeight);
31538 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31540 var pos = this.el.getBox(true);
31545 var maxX = pos.right;
31547 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31549 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31551 Roo.each(queue, function(box, k){
31553 Roo.each(box, function(b, kk){
31555 b.el.position('absolute');
31557 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31558 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31560 if(b.size == 'md-left' || b.size == 'md-right'){
31561 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31562 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31565 b.el.setWidth(width);
31566 b.el.setHeight(height);
31574 var positions = [];
31576 switch (box.length){
31578 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31581 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31584 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31587 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31593 Roo.each(box, function(b,kk){
31595 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31597 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31605 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31607 Roo.each(eItems, function(b,k){
31609 b.size = (k == 0) ? 'sm' : 'xs';
31610 b.x = (k == 0) ? 2 : 1;
31611 b.y = (k == 0) ? 2 : 1;
31613 b.el.position('absolute');
31615 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31617 b.el.setWidth(width);
31619 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31621 b.el.setHeight(height);
31625 var positions = [];
31628 x : maxX - this.unitWidth * 2 - this.gutter,
31633 x : maxX - this.unitWidth,
31634 y : minY + (this.unitWidth + this.gutter) * 2
31638 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31642 Roo.each(eItems, function(b,k){
31644 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31650 getVerticalOneBoxColPositions : function(x, y, box)
31654 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31656 if(box[0].size == 'md-left'){
31660 if(box[0].size == 'md-right'){
31665 x : x + (this.unitWidth + this.gutter) * rand,
31672 getVerticalTwoBoxColPositions : function(x, y, box)
31676 if(box[0].size == 'xs'){
31680 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31684 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31698 x : x + (this.unitWidth + this.gutter) * 2,
31699 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31706 getVerticalThreeBoxColPositions : function(x, y, box)
31710 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31718 x : x + (this.unitWidth + this.gutter) * 1,
31723 x : x + (this.unitWidth + this.gutter) * 2,
31731 if(box[0].size == 'xs' && box[1].size == 'xs'){
31740 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31744 x : x + (this.unitWidth + this.gutter) * 1,
31758 x : x + (this.unitWidth + this.gutter) * 2,
31763 x : x + (this.unitWidth + this.gutter) * 2,
31764 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31771 getVerticalFourBoxColPositions : function(x, y, box)
31775 if(box[0].size == 'xs'){
31784 y : y + (this.unitHeight + this.gutter) * 1
31789 y : y + (this.unitHeight + this.gutter) * 2
31793 x : x + (this.unitWidth + this.gutter) * 1,
31807 x : x + (this.unitWidth + this.gutter) * 2,
31812 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31813 y : y + (this.unitHeight + this.gutter) * 1
31817 x : x + (this.unitWidth + this.gutter) * 2,
31818 y : y + (this.unitWidth + this.gutter) * 2
31825 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31829 if(box[0].size == 'md-left'){
31831 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31838 if(box[0].size == 'md-right'){
31840 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31841 y : minY + (this.unitWidth + this.gutter) * 1
31847 var rand = Math.floor(Math.random() * (4 - box[0].y));
31850 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31851 y : minY + (this.unitWidth + this.gutter) * rand
31858 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31862 if(box[0].size == 'xs'){
31865 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31870 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31871 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31879 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31884 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31885 y : minY + (this.unitWidth + this.gutter) * 2
31892 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31896 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31899 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31904 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31905 y : minY + (this.unitWidth + this.gutter) * 1
31909 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31910 y : minY + (this.unitWidth + this.gutter) * 2
31917 if(box[0].size == 'xs' && box[1].size == 'xs'){
31920 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31925 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31930 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31931 y : minY + (this.unitWidth + this.gutter) * 1
31939 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31944 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31945 y : minY + (this.unitWidth + this.gutter) * 2
31949 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31950 y : minY + (this.unitWidth + this.gutter) * 2
31957 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31961 if(box[0].size == 'xs'){
31964 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31969 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31974 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),
31979 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31980 y : minY + (this.unitWidth + this.gutter) * 1
31988 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31993 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31994 y : minY + (this.unitWidth + this.gutter) * 2
31998 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31999 y : minY + (this.unitWidth + this.gutter) * 2
32003 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),
32004 y : minY + (this.unitWidth + this.gutter) * 2
32012 * remove a Masonry Brick
32013 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32015 removeBrick : function(brick_id)
32021 for (var i = 0; i<this.bricks.length; i++) {
32022 if (this.bricks[i].id == brick_id) {
32023 this.bricks.splice(i,1);
32024 this.el.dom.removeChild(Roo.get(brick_id).dom);
32031 * adds a Masonry Brick
32032 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32034 addBrick : function(cfg)
32036 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32037 //this.register(cn);
32038 cn.parentId = this.id;
32039 cn.render(this.el);
32044 * register a Masonry Brick
32045 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32048 register : function(brick)
32050 this.bricks.push(brick);
32051 brick.masonryId = this.id;
32055 * clear all the Masonry Brick
32057 clearAll : function()
32060 //this.getChildContainer().dom.innerHTML = "";
32061 this.el.dom.innerHTML = '';
32064 getSelected : function()
32066 if (!this.selectedBrick) {
32070 return this.selectedBrick;
32074 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32078 * register a Masonry Layout
32079 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32082 register : function(layout)
32084 this.groups[layout.id] = layout;
32087 * fetch a Masonry Layout based on the masonry layout ID
32088 * @param {string} the masonry layout to add
32089 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32092 get: function(layout_id) {
32093 if (typeof(this.groups[layout_id]) == 'undefined') {
32096 return this.groups[layout_id] ;
32108 * http://masonry.desandro.com
32110 * The idea is to render all the bricks based on vertical width...
32112 * The original code extends 'outlayer' - we might need to use that....
32118 * @class Roo.bootstrap.LayoutMasonryAuto
32119 * @extends Roo.bootstrap.Component
32120 * Bootstrap Layout Masonry class
32123 * Create a new Element
32124 * @param {Object} config The config object
32127 Roo.bootstrap.LayoutMasonryAuto = function(config){
32128 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32131 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32134 * @cfg {Boolean} isFitWidth - resize the width..
32136 isFitWidth : false, // options..
32138 * @cfg {Boolean} isOriginLeft = left align?
32140 isOriginLeft : true,
32142 * @cfg {Boolean} isOriginTop = top align?
32144 isOriginTop : false,
32146 * @cfg {Boolean} isLayoutInstant = no animation?
32148 isLayoutInstant : false, // needed?
32150 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32152 isResizingContainer : true,
32154 * @cfg {Number} columnWidth width of the columns
32160 * @cfg {Number} maxCols maximum number of columns
32165 * @cfg {Number} padHeight padding below box..
32171 * @cfg {Boolean} isAutoInitial defalut true
32174 isAutoInitial : true,
32180 initialColumnWidth : 0,
32181 currentSize : null,
32183 colYs : null, // array.
32190 bricks: null, //CompositeElement
32191 cols : 0, // array?
32192 // element : null, // wrapped now this.el
32193 _isLayoutInited : null,
32196 getAutoCreate : function(){
32200 cls: 'blog-masonary-wrapper ' + this.cls,
32202 cls : 'mas-boxes masonary'
32209 getChildContainer: function( )
32211 if (this.boxesEl) {
32212 return this.boxesEl;
32215 this.boxesEl = this.el.select('.mas-boxes').first();
32217 return this.boxesEl;
32221 initEvents : function()
32225 if(this.isAutoInitial){
32226 Roo.log('hook children rendered');
32227 this.on('childrenrendered', function() {
32228 Roo.log('children rendered');
32235 initial : function()
32237 this.reloadItems();
32239 this.currentSize = this.el.getBox(true);
32241 /// was window resize... - let's see if this works..
32242 Roo.EventManager.onWindowResize(this.resize, this);
32244 if(!this.isAutoInitial){
32249 this.layout.defer(500,this);
32252 reloadItems: function()
32254 this.bricks = this.el.select('.masonry-brick', true);
32256 this.bricks.each(function(b) {
32257 //Roo.log(b.getSize());
32258 if (!b.attr('originalwidth')) {
32259 b.attr('originalwidth', b.getSize().width);
32264 Roo.log(this.bricks.elements.length);
32267 resize : function()
32270 var cs = this.el.getBox(true);
32272 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32273 Roo.log("no change in with or X");
32276 this.currentSize = cs;
32280 layout : function()
32283 this._resetLayout();
32284 //this._manageStamps();
32286 // don't animate first layout
32287 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32288 this.layoutItems( isInstant );
32290 // flag for initalized
32291 this._isLayoutInited = true;
32294 layoutItems : function( isInstant )
32296 //var items = this._getItemsForLayout( this.items );
32297 // original code supports filtering layout items.. we just ignore it..
32299 this._layoutItems( this.bricks , isInstant );
32301 this._postLayout();
32303 _layoutItems : function ( items , isInstant)
32305 //this.fireEvent( 'layout', this, items );
32308 if ( !items || !items.elements.length ) {
32309 // no items, emit event with empty array
32314 items.each(function(item) {
32315 Roo.log("layout item");
32317 // get x/y object from method
32318 var position = this._getItemLayoutPosition( item );
32320 position.item = item;
32321 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32322 queue.push( position );
32325 this._processLayoutQueue( queue );
32327 /** Sets position of item in DOM
32328 * @param {Element} item
32329 * @param {Number} x - horizontal position
32330 * @param {Number} y - vertical position
32331 * @param {Boolean} isInstant - disables transitions
32333 _processLayoutQueue : function( queue )
32335 for ( var i=0, len = queue.length; i < len; i++ ) {
32336 var obj = queue[i];
32337 obj.item.position('absolute');
32338 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32344 * Any logic you want to do after each layout,
32345 * i.e. size the container
32347 _postLayout : function()
32349 this.resizeContainer();
32352 resizeContainer : function()
32354 if ( !this.isResizingContainer ) {
32357 var size = this._getContainerSize();
32359 this.el.setSize(size.width,size.height);
32360 this.boxesEl.setSize(size.width,size.height);
32366 _resetLayout : function()
32368 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32369 this.colWidth = this.el.getWidth();
32370 //this.gutter = this.el.getWidth();
32372 this.measureColumns();
32378 this.colYs.push( 0 );
32384 measureColumns : function()
32386 this.getContainerWidth();
32387 // if columnWidth is 0, default to outerWidth of first item
32388 if ( !this.columnWidth ) {
32389 var firstItem = this.bricks.first();
32390 Roo.log(firstItem);
32391 this.columnWidth = this.containerWidth;
32392 if (firstItem && firstItem.attr('originalwidth') ) {
32393 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32395 // columnWidth fall back to item of first element
32396 Roo.log("set column width?");
32397 this.initialColumnWidth = this.columnWidth ;
32399 // if first elem has no width, default to size of container
32404 if (this.initialColumnWidth) {
32405 this.columnWidth = this.initialColumnWidth;
32410 // column width is fixed at the top - however if container width get's smaller we should
32413 // this bit calcs how man columns..
32415 var columnWidth = this.columnWidth += this.gutter;
32417 // calculate columns
32418 var containerWidth = this.containerWidth + this.gutter;
32420 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32421 // fix rounding errors, typically with gutters
32422 var excess = columnWidth - containerWidth % columnWidth;
32425 // if overshoot is less than a pixel, round up, otherwise floor it
32426 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32427 cols = Math[ mathMethod ]( cols );
32428 this.cols = Math.max( cols, 1 );
32429 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32431 // padding positioning..
32432 var totalColWidth = this.cols * this.columnWidth;
32433 var padavail = this.containerWidth - totalColWidth;
32434 // so for 2 columns - we need 3 'pads'
32436 var padNeeded = (1+this.cols) * this.padWidth;
32438 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32440 this.columnWidth += padExtra
32441 //this.padWidth = Math.floor(padavail / ( this.cols));
32443 // adjust colum width so that padding is fixed??
32445 // we have 3 columns ... total = width * 3
32446 // we have X left over... that should be used by
32448 //if (this.expandC) {
32456 getContainerWidth : function()
32458 /* // container is parent if fit width
32459 var container = this.isFitWidth ? this.element.parentNode : this.element;
32460 // check that this.size and size are there
32461 // IE8 triggers resize on body size change, so they might not be
32463 var size = getSize( container ); //FIXME
32464 this.containerWidth = size && size.innerWidth; //FIXME
32467 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32471 _getItemLayoutPosition : function( item ) // what is item?
32473 // we resize the item to our columnWidth..
32475 item.setWidth(this.columnWidth);
32476 item.autoBoxAdjust = false;
32478 var sz = item.getSize();
32480 // how many columns does this brick span
32481 var remainder = this.containerWidth % this.columnWidth;
32483 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32484 // round if off by 1 pixel, otherwise use ceil
32485 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32486 colSpan = Math.min( colSpan, this.cols );
32488 // normally this should be '1' as we dont' currently allow multi width columns..
32490 var colGroup = this._getColGroup( colSpan );
32491 // get the minimum Y value from the columns
32492 var minimumY = Math.min.apply( Math, colGroup );
32493 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32495 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32497 // position the brick
32499 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32500 y: this.currentSize.y + minimumY + this.padHeight
32504 // apply setHeight to necessary columns
32505 var setHeight = minimumY + sz.height + this.padHeight;
32506 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32508 var setSpan = this.cols + 1 - colGroup.length;
32509 for ( var i = 0; i < setSpan; i++ ) {
32510 this.colYs[ shortColIndex + i ] = setHeight ;
32517 * @param {Number} colSpan - number of columns the element spans
32518 * @returns {Array} colGroup
32520 _getColGroup : function( colSpan )
32522 if ( colSpan < 2 ) {
32523 // if brick spans only one column, use all the column Ys
32528 // how many different places could this brick fit horizontally
32529 var groupCount = this.cols + 1 - colSpan;
32530 // for each group potential horizontal position
32531 for ( var i = 0; i < groupCount; i++ ) {
32532 // make an array of colY values for that one group
32533 var groupColYs = this.colYs.slice( i, i + colSpan );
32534 // and get the max value of the array
32535 colGroup[i] = Math.max.apply( Math, groupColYs );
32540 _manageStamp : function( stamp )
32542 var stampSize = stamp.getSize();
32543 var offset = stamp.getBox();
32544 // get the columns that this stamp affects
32545 var firstX = this.isOriginLeft ? offset.x : offset.right;
32546 var lastX = firstX + stampSize.width;
32547 var firstCol = Math.floor( firstX / this.columnWidth );
32548 firstCol = Math.max( 0, firstCol );
32550 var lastCol = Math.floor( lastX / this.columnWidth );
32551 // lastCol should not go over if multiple of columnWidth #425
32552 lastCol -= lastX % this.columnWidth ? 0 : 1;
32553 lastCol = Math.min( this.cols - 1, lastCol );
32555 // set colYs to bottom of the stamp
32556 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32559 for ( var i = firstCol; i <= lastCol; i++ ) {
32560 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32565 _getContainerSize : function()
32567 this.maxY = Math.max.apply( Math, this.colYs );
32572 if ( this.isFitWidth ) {
32573 size.width = this._getContainerFitWidth();
32579 _getContainerFitWidth : function()
32581 var unusedCols = 0;
32582 // count unused columns
32585 if ( this.colYs[i] !== 0 ) {
32590 // fit container to columns that have been used
32591 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32594 needsResizeLayout : function()
32596 var previousWidth = this.containerWidth;
32597 this.getContainerWidth();
32598 return previousWidth !== this.containerWidth;
32613 * @class Roo.bootstrap.MasonryBrick
32614 * @extends Roo.bootstrap.Component
32615 * Bootstrap MasonryBrick class
32618 * Create a new MasonryBrick
32619 * @param {Object} config The config object
32622 Roo.bootstrap.MasonryBrick = function(config){
32624 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32626 Roo.bootstrap.MasonryBrick.register(this);
32632 * When a MasonryBrick is clcik
32633 * @param {Roo.bootstrap.MasonryBrick} this
32634 * @param {Roo.EventObject} e
32640 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32643 * @cfg {String} title
32647 * @cfg {String} html
32651 * @cfg {String} bgimage
32655 * @cfg {String} videourl
32659 * @cfg {String} cls
32663 * @cfg {String} href
32667 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32672 * @cfg {String} placetitle (center|bottom)
32677 * @cfg {Boolean} isFitContainer defalut true
32679 isFitContainer : true,
32682 * @cfg {Boolean} preventDefault defalut false
32684 preventDefault : false,
32687 * @cfg {Boolean} inverse defalut false
32689 maskInverse : false,
32691 getAutoCreate : function()
32693 if(!this.isFitContainer){
32694 return this.getSplitAutoCreate();
32697 var cls = 'masonry-brick masonry-brick-full';
32699 if(this.href.length){
32700 cls += ' masonry-brick-link';
32703 if(this.bgimage.length){
32704 cls += ' masonry-brick-image';
32707 if(this.maskInverse){
32708 cls += ' mask-inverse';
32711 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32712 cls += ' enable-mask';
32716 cls += ' masonry-' + this.size + '-brick';
32719 if(this.placetitle.length){
32721 switch (this.placetitle) {
32723 cls += ' masonry-center-title';
32726 cls += ' masonry-bottom-title';
32733 if(!this.html.length && !this.bgimage.length){
32734 cls += ' masonry-center-title';
32737 if(!this.html.length && this.bgimage.length){
32738 cls += ' masonry-bottom-title';
32743 cls += ' ' + this.cls;
32747 tag: (this.href.length) ? 'a' : 'div',
32752 cls: 'masonry-brick-mask'
32756 cls: 'masonry-brick-paragraph',
32762 if(this.href.length){
32763 cfg.href = this.href;
32766 var cn = cfg.cn[1].cn;
32768 if(this.title.length){
32771 cls: 'masonry-brick-title',
32776 if(this.html.length){
32779 cls: 'masonry-brick-text',
32784 if (!this.title.length && !this.html.length) {
32785 cfg.cn[1].cls += ' hide';
32788 if(this.bgimage.length){
32791 cls: 'masonry-brick-image-view',
32796 if(this.videourl.length){
32797 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32798 // youtube support only?
32801 cls: 'masonry-brick-image-view',
32804 allowfullscreen : true
32812 getSplitAutoCreate : function()
32814 var cls = 'masonry-brick masonry-brick-split';
32816 if(this.href.length){
32817 cls += ' masonry-brick-link';
32820 if(this.bgimage.length){
32821 cls += ' masonry-brick-image';
32825 cls += ' masonry-' + this.size + '-brick';
32828 switch (this.placetitle) {
32830 cls += ' masonry-center-title';
32833 cls += ' masonry-bottom-title';
32836 if(!this.bgimage.length){
32837 cls += ' masonry-center-title';
32840 if(this.bgimage.length){
32841 cls += ' masonry-bottom-title';
32847 cls += ' ' + this.cls;
32851 tag: (this.href.length) ? 'a' : 'div',
32856 cls: 'masonry-brick-split-head',
32860 cls: 'masonry-brick-paragraph',
32867 cls: 'masonry-brick-split-body',
32873 if(this.href.length){
32874 cfg.href = this.href;
32877 if(this.title.length){
32878 cfg.cn[0].cn[0].cn.push({
32880 cls: 'masonry-brick-title',
32885 if(this.html.length){
32886 cfg.cn[1].cn.push({
32888 cls: 'masonry-brick-text',
32893 if(this.bgimage.length){
32894 cfg.cn[0].cn.push({
32896 cls: 'masonry-brick-image-view',
32901 if(this.videourl.length){
32902 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32903 // youtube support only?
32904 cfg.cn[0].cn.cn.push({
32906 cls: 'masonry-brick-image-view',
32909 allowfullscreen : true
32916 initEvents: function()
32918 switch (this.size) {
32951 this.el.on('touchstart', this.onTouchStart, this);
32952 this.el.on('touchmove', this.onTouchMove, this);
32953 this.el.on('touchend', this.onTouchEnd, this);
32954 this.el.on('contextmenu', this.onContextMenu, this);
32956 this.el.on('mouseenter' ,this.enter, this);
32957 this.el.on('mouseleave', this.leave, this);
32958 this.el.on('click', this.onClick, this);
32961 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32962 this.parent().bricks.push(this);
32967 onClick: function(e, el)
32969 var time = this.endTimer - this.startTimer;
32970 // Roo.log(e.preventDefault());
32973 e.preventDefault();
32978 if(!this.preventDefault){
32982 e.preventDefault();
32984 if (this.activeClass != '') {
32985 this.selectBrick();
32988 this.fireEvent('click', this, e);
32991 enter: function(e, el)
32993 e.preventDefault();
32995 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32999 if(this.bgimage.length && this.html.length){
33000 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33004 leave: function(e, el)
33006 e.preventDefault();
33008 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33012 if(this.bgimage.length && this.html.length){
33013 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33017 onTouchStart: function(e, el)
33019 // e.preventDefault();
33021 this.touchmoved = false;
33023 if(!this.isFitContainer){
33027 if(!this.bgimage.length || !this.html.length){
33031 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33033 this.timer = new Date().getTime();
33037 onTouchMove: function(e, el)
33039 this.touchmoved = true;
33042 onContextMenu : function(e,el)
33044 e.preventDefault();
33045 e.stopPropagation();
33049 onTouchEnd: function(e, el)
33051 // e.preventDefault();
33053 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33060 if(!this.bgimage.length || !this.html.length){
33062 if(this.href.length){
33063 window.location.href = this.href;
33069 if(!this.isFitContainer){
33073 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33075 window.location.href = this.href;
33078 //selection on single brick only
33079 selectBrick : function() {
33081 if (!this.parentId) {
33085 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33086 var index = m.selectedBrick.indexOf(this.id);
33089 m.selectedBrick.splice(index,1);
33090 this.el.removeClass(this.activeClass);
33094 for(var i = 0; i < m.selectedBrick.length; i++) {
33095 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33096 b.el.removeClass(b.activeClass);
33099 m.selectedBrick = [];
33101 m.selectedBrick.push(this.id);
33102 this.el.addClass(this.activeClass);
33106 isSelected : function(){
33107 return this.el.hasClass(this.activeClass);
33112 Roo.apply(Roo.bootstrap.MasonryBrick, {
33115 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33117 * register a Masonry Brick
33118 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33121 register : function(brick)
33123 //this.groups[brick.id] = brick;
33124 this.groups.add(brick.id, brick);
33127 * fetch a masonry brick based on the masonry brick ID
33128 * @param {string} the masonry brick to add
33129 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33132 get: function(brick_id)
33134 // if (typeof(this.groups[brick_id]) == 'undefined') {
33137 // return this.groups[brick_id] ;
33139 if(this.groups.key(brick_id)) {
33140 return this.groups.key(brick_id);
33158 * @class Roo.bootstrap.Brick
33159 * @extends Roo.bootstrap.Component
33160 * Bootstrap Brick class
33163 * Create a new Brick
33164 * @param {Object} config The config object
33167 Roo.bootstrap.Brick = function(config){
33168 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33174 * When a Brick is click
33175 * @param {Roo.bootstrap.Brick} this
33176 * @param {Roo.EventObject} e
33182 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33185 * @cfg {String} title
33189 * @cfg {String} html
33193 * @cfg {String} bgimage
33197 * @cfg {String} cls
33201 * @cfg {String} href
33205 * @cfg {String} video
33209 * @cfg {Boolean} square
33213 getAutoCreate : function()
33215 var cls = 'roo-brick';
33217 if(this.href.length){
33218 cls += ' roo-brick-link';
33221 if(this.bgimage.length){
33222 cls += ' roo-brick-image';
33225 if(!this.html.length && !this.bgimage.length){
33226 cls += ' roo-brick-center-title';
33229 if(!this.html.length && this.bgimage.length){
33230 cls += ' roo-brick-bottom-title';
33234 cls += ' ' + this.cls;
33238 tag: (this.href.length) ? 'a' : 'div',
33243 cls: 'roo-brick-paragraph',
33249 if(this.href.length){
33250 cfg.href = this.href;
33253 var cn = cfg.cn[0].cn;
33255 if(this.title.length){
33258 cls: 'roo-brick-title',
33263 if(this.html.length){
33266 cls: 'roo-brick-text',
33273 if(this.bgimage.length){
33276 cls: 'roo-brick-image-view',
33284 initEvents: function()
33286 if(this.title.length || this.html.length){
33287 this.el.on('mouseenter' ,this.enter, this);
33288 this.el.on('mouseleave', this.leave, this);
33291 Roo.EventManager.onWindowResize(this.resize, this);
33293 if(this.bgimage.length){
33294 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33295 this.imageEl.on('load', this.onImageLoad, this);
33302 onImageLoad : function()
33307 resize : function()
33309 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33311 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33313 if(this.bgimage.length){
33314 var image = this.el.select('.roo-brick-image-view', true).first();
33316 image.setWidth(paragraph.getWidth());
33319 image.setHeight(paragraph.getWidth());
33322 this.el.setHeight(image.getHeight());
33323 paragraph.setHeight(image.getHeight());
33329 enter: function(e, el)
33331 e.preventDefault();
33333 if(this.bgimage.length){
33334 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33335 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33339 leave: function(e, el)
33341 e.preventDefault();
33343 if(this.bgimage.length){
33344 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33345 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33360 * @class Roo.bootstrap.NumberField
33361 * @extends Roo.bootstrap.Input
33362 * Bootstrap NumberField class
33368 * Create a new NumberField
33369 * @param {Object} config The config object
33372 Roo.bootstrap.NumberField = function(config){
33373 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33376 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33379 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33381 allowDecimals : true,
33383 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33385 decimalSeparator : ".",
33387 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33389 decimalPrecision : 2,
33391 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33393 allowNegative : true,
33396 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33400 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33402 minValue : Number.NEGATIVE_INFINITY,
33404 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33406 maxValue : Number.MAX_VALUE,
33408 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33410 minText : "The minimum value for this field is {0}",
33412 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33414 maxText : "The maximum value for this field is {0}",
33416 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33417 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33419 nanText : "{0} is not a valid number",
33421 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33423 thousandsDelimiter : false,
33425 * @cfg {String} valueAlign alignment of value
33427 valueAlign : "left",
33429 getAutoCreate : function()
33431 var hiddenInput = {
33435 cls: 'hidden-number-input'
33439 hiddenInput.name = this.name;
33444 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33446 this.name = hiddenInput.name;
33448 if(cfg.cn.length > 0) {
33449 cfg.cn.push(hiddenInput);
33456 initEvents : function()
33458 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33460 var allowed = "0123456789";
33462 if(this.allowDecimals){
33463 allowed += this.decimalSeparator;
33466 if(this.allowNegative){
33470 if(this.thousandsDelimiter) {
33474 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33476 var keyPress = function(e){
33478 var k = e.getKey();
33480 var c = e.getCharCode();
33483 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33484 allowed.indexOf(String.fromCharCode(c)) === -1
33490 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33494 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33499 this.el.on("keypress", keyPress, this);
33502 validateValue : function(value)
33505 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33509 var num = this.parseValue(value);
33512 this.markInvalid(String.format(this.nanText, value));
33516 if(num < this.minValue){
33517 this.markInvalid(String.format(this.minText, this.minValue));
33521 if(num > this.maxValue){
33522 this.markInvalid(String.format(this.maxText, this.maxValue));
33529 getValue : function()
33531 var v = this.hiddenEl().getValue();
33533 return this.fixPrecision(this.parseValue(v));
33536 parseValue : function(value)
33538 if(this.thousandsDelimiter) {
33540 r = new RegExp(",", "g");
33541 value = value.replace(r, "");
33544 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33545 return isNaN(value) ? '' : value;
33548 fixPrecision : function(value)
33550 if(this.thousandsDelimiter) {
33552 r = new RegExp(",", "g");
33553 value = value.replace(r, "");
33556 var nan = isNaN(value);
33558 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33559 return nan ? '' : value;
33561 return parseFloat(value).toFixed(this.decimalPrecision);
33564 setValue : function(v)
33566 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33572 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33574 this.inputEl().dom.value = (v == '') ? '' :
33575 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33577 if(!this.allowZero && v === '0') {
33578 this.hiddenEl().dom.value = '';
33579 this.inputEl().dom.value = '';
33586 decimalPrecisionFcn : function(v)
33588 return Math.floor(v);
33591 beforeBlur : function()
33593 var v = this.parseValue(this.getRawValue());
33595 if(v || v === 0 || v === ''){
33600 hiddenEl : function()
33602 return this.el.select('input.hidden-number-input',true).first();
33614 * @class Roo.bootstrap.DocumentSlider
33615 * @extends Roo.bootstrap.Component
33616 * Bootstrap DocumentSlider class
33619 * Create a new DocumentViewer
33620 * @param {Object} config The config object
33623 Roo.bootstrap.DocumentSlider = function(config){
33624 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33631 * Fire after initEvent
33632 * @param {Roo.bootstrap.DocumentSlider} this
33637 * Fire after update
33638 * @param {Roo.bootstrap.DocumentSlider} this
33644 * @param {Roo.bootstrap.DocumentSlider} this
33650 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33656 getAutoCreate : function()
33660 cls : 'roo-document-slider',
33664 cls : 'roo-document-slider-header',
33668 cls : 'roo-document-slider-header-title'
33674 cls : 'roo-document-slider-body',
33678 cls : 'roo-document-slider-prev',
33682 cls : 'fa fa-chevron-left'
33688 cls : 'roo-document-slider-thumb',
33692 cls : 'roo-document-slider-image'
33698 cls : 'roo-document-slider-next',
33702 cls : 'fa fa-chevron-right'
33714 initEvents : function()
33716 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33717 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33719 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33720 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33722 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33723 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33725 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33726 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33728 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33729 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33731 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33732 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33734 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33735 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33737 this.thumbEl.on('click', this.onClick, this);
33739 this.prevIndicator.on('click', this.prev, this);
33741 this.nextIndicator.on('click', this.next, this);
33745 initial : function()
33747 if(this.files.length){
33748 this.indicator = 1;
33752 this.fireEvent('initial', this);
33755 update : function()
33757 this.imageEl.attr('src', this.files[this.indicator - 1]);
33759 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33761 this.prevIndicator.show();
33763 if(this.indicator == 1){
33764 this.prevIndicator.hide();
33767 this.nextIndicator.show();
33769 if(this.indicator == this.files.length){
33770 this.nextIndicator.hide();
33773 this.thumbEl.scrollTo('top');
33775 this.fireEvent('update', this);
33778 onClick : function(e)
33780 e.preventDefault();
33782 this.fireEvent('click', this);
33787 e.preventDefault();
33789 this.indicator = Math.max(1, this.indicator - 1);
33796 e.preventDefault();
33798 this.indicator = Math.min(this.files.length, this.indicator + 1);
33812 * @class Roo.bootstrap.RadioSet
33813 * @extends Roo.bootstrap.Input
33814 * Bootstrap RadioSet class
33815 * @cfg {String} indicatorpos (left|right) default left
33816 * @cfg {Boolean} inline (true|false) inline the element (default true)
33817 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33819 * Create a new RadioSet
33820 * @param {Object} config The config object
33823 Roo.bootstrap.RadioSet = function(config){
33825 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33829 Roo.bootstrap.RadioSet.register(this);
33834 * Fires when the element is checked or unchecked.
33835 * @param {Roo.bootstrap.RadioSet} this This radio
33836 * @param {Roo.bootstrap.Radio} item The checked item
33841 * Fires when the element is click.
33842 * @param {Roo.bootstrap.RadioSet} this This radio set
33843 * @param {Roo.bootstrap.Radio} item The checked item
33844 * @param {Roo.EventObject} e The event object
33851 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33859 indicatorpos : 'left',
33861 getAutoCreate : function()
33865 cls : 'roo-radio-set-label',
33869 html : this.fieldLabel
33874 if(this.indicatorpos == 'left'){
33877 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33878 tooltip : 'This field is required'
33883 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33884 tooltip : 'This field is required'
33890 cls : 'roo-radio-set-items'
33893 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33895 if (align === 'left' && this.fieldLabel.length) {
33898 cls : "roo-radio-set-right",
33904 if(this.labelWidth > 12){
33905 label.style = "width: " + this.labelWidth + 'px';
33908 if(this.labelWidth < 13 && this.labelmd == 0){
33909 this.labelmd = this.labelWidth;
33912 if(this.labellg > 0){
33913 label.cls += ' col-lg-' + this.labellg;
33914 items.cls += ' col-lg-' + (12 - this.labellg);
33917 if(this.labelmd > 0){
33918 label.cls += ' col-md-' + this.labelmd;
33919 items.cls += ' col-md-' + (12 - this.labelmd);
33922 if(this.labelsm > 0){
33923 label.cls += ' col-sm-' + this.labelsm;
33924 items.cls += ' col-sm-' + (12 - this.labelsm);
33927 if(this.labelxs > 0){
33928 label.cls += ' col-xs-' + this.labelxs;
33929 items.cls += ' col-xs-' + (12 - this.labelxs);
33935 cls : 'roo-radio-set',
33939 cls : 'roo-radio-set-input',
33942 value : this.value ? this.value : ''
33949 if(this.weight.length){
33950 cfg.cls += ' roo-radio-' + this.weight;
33954 cfg.cls += ' roo-radio-set-inline';
33958 ['xs','sm','md','lg'].map(function(size){
33959 if (settings[size]) {
33960 cfg.cls += ' col-' + size + '-' + settings[size];
33968 initEvents : function()
33970 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33971 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33973 if(!this.fieldLabel.length){
33974 this.labelEl.hide();
33977 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33978 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33980 this.indicator = this.indicatorEl();
33982 if(this.indicator){
33983 this.indicator.addClass('invisible');
33986 this.originalValue = this.getValue();
33990 inputEl: function ()
33992 return this.el.select('.roo-radio-set-input', true).first();
33995 getChildContainer : function()
33997 return this.itemsEl;
34000 register : function(item)
34002 this.radioes.push(item);
34006 validate : function()
34008 if(this.getVisibilityEl().hasClass('hidden')){
34014 Roo.each(this.radioes, function(i){
34023 if(this.allowBlank) {
34027 if(this.disabled || valid){
34032 this.markInvalid();
34037 markValid : function()
34039 if(this.labelEl.isVisible(true)){
34040 this.indicatorEl().removeClass('visible');
34041 this.indicatorEl().addClass('invisible');
34044 this.el.removeClass([this.invalidClass, this.validClass]);
34045 this.el.addClass(this.validClass);
34047 this.fireEvent('valid', this);
34050 markInvalid : function(msg)
34052 if(this.allowBlank || this.disabled){
34056 if(this.labelEl.isVisible(true)){
34057 this.indicatorEl().removeClass('invisible');
34058 this.indicatorEl().addClass('visible');
34061 this.el.removeClass([this.invalidClass, this.validClass]);
34062 this.el.addClass(this.invalidClass);
34064 this.fireEvent('invalid', this, msg);
34068 setValue : function(v, suppressEvent)
34070 if(this.value === v){
34077 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34080 Roo.each(this.radioes, function(i){
34082 i.el.removeClass('checked');
34085 Roo.each(this.radioes, function(i){
34087 if(i.value === v || i.value.toString() === v.toString()){
34089 i.el.addClass('checked');
34091 if(suppressEvent !== true){
34092 this.fireEvent('check', this, i);
34103 clearInvalid : function(){
34105 if(!this.el || this.preventMark){
34109 this.el.removeClass([this.invalidClass]);
34111 this.fireEvent('valid', this);
34116 Roo.apply(Roo.bootstrap.RadioSet, {
34120 register : function(set)
34122 this.groups[set.name] = set;
34125 get: function(name)
34127 if (typeof(this.groups[name]) == 'undefined') {
34131 return this.groups[name] ;
34137 * Ext JS Library 1.1.1
34138 * Copyright(c) 2006-2007, Ext JS, LLC.
34140 * Originally Released Under LGPL - original licence link has changed is not relivant.
34143 * <script type="text/javascript">
34148 * @class Roo.bootstrap.SplitBar
34149 * @extends Roo.util.Observable
34150 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34154 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34155 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34156 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34157 split.minSize = 100;
34158 split.maxSize = 600;
34159 split.animate = true;
34160 split.on('moved', splitterMoved);
34163 * Create a new SplitBar
34164 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34165 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34166 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34167 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34168 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34169 position of the SplitBar).
34171 Roo.bootstrap.SplitBar = function(cfg){
34176 // dragElement : elm
34177 // resizingElement: el,
34179 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34180 // placement : Roo.bootstrap.SplitBar.LEFT ,
34181 // existingProxy ???
34184 this.el = Roo.get(cfg.dragElement, true);
34185 this.el.dom.unselectable = "on";
34187 this.resizingEl = Roo.get(cfg.resizingElement, true);
34191 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34192 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34195 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34198 * The minimum size of the resizing element. (Defaults to 0)
34204 * The maximum size of the resizing element. (Defaults to 2000)
34207 this.maxSize = 2000;
34210 * Whether to animate the transition to the new size
34213 this.animate = false;
34216 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34219 this.useShim = false;
34224 if(!cfg.existingProxy){
34226 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34228 this.proxy = Roo.get(cfg.existingProxy).dom;
34231 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34234 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34237 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34240 this.dragSpecs = {};
34243 * @private The adapter to use to positon and resize elements
34245 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34246 this.adapter.init(this);
34248 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34250 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34251 this.el.addClass("roo-splitbar-h");
34254 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34255 this.el.addClass("roo-splitbar-v");
34261 * Fires when the splitter is moved (alias for {@link #event-moved})
34262 * @param {Roo.bootstrap.SplitBar} this
34263 * @param {Number} newSize the new width or height
34268 * Fires when the splitter is moved
34269 * @param {Roo.bootstrap.SplitBar} this
34270 * @param {Number} newSize the new width or height
34274 * @event beforeresize
34275 * Fires before the splitter is dragged
34276 * @param {Roo.bootstrap.SplitBar} this
34278 "beforeresize" : true,
34280 "beforeapply" : true
34283 Roo.util.Observable.call(this);
34286 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34287 onStartProxyDrag : function(x, y){
34288 this.fireEvent("beforeresize", this);
34290 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34292 o.enableDisplayMode("block");
34293 // all splitbars share the same overlay
34294 Roo.bootstrap.SplitBar.prototype.overlay = o;
34296 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34297 this.overlay.show();
34298 Roo.get(this.proxy).setDisplayed("block");
34299 var size = this.adapter.getElementSize(this);
34300 this.activeMinSize = this.getMinimumSize();;
34301 this.activeMaxSize = this.getMaximumSize();;
34302 var c1 = size - this.activeMinSize;
34303 var c2 = Math.max(this.activeMaxSize - size, 0);
34304 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34305 this.dd.resetConstraints();
34306 this.dd.setXConstraint(
34307 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34308 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34310 this.dd.setYConstraint(0, 0);
34312 this.dd.resetConstraints();
34313 this.dd.setXConstraint(0, 0);
34314 this.dd.setYConstraint(
34315 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34316 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34319 this.dragSpecs.startSize = size;
34320 this.dragSpecs.startPoint = [x, y];
34321 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34325 * @private Called after the drag operation by the DDProxy
34327 onEndProxyDrag : function(e){
34328 Roo.get(this.proxy).setDisplayed(false);
34329 var endPoint = Roo.lib.Event.getXY(e);
34331 this.overlay.hide();
34334 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34335 newSize = this.dragSpecs.startSize +
34336 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34337 endPoint[0] - this.dragSpecs.startPoint[0] :
34338 this.dragSpecs.startPoint[0] - endPoint[0]
34341 newSize = this.dragSpecs.startSize +
34342 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34343 endPoint[1] - this.dragSpecs.startPoint[1] :
34344 this.dragSpecs.startPoint[1] - endPoint[1]
34347 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34348 if(newSize != this.dragSpecs.startSize){
34349 if(this.fireEvent('beforeapply', this, newSize) !== false){
34350 this.adapter.setElementSize(this, newSize);
34351 this.fireEvent("moved", this, newSize);
34352 this.fireEvent("resize", this, newSize);
34358 * Get the adapter this SplitBar uses
34359 * @return The adapter object
34361 getAdapter : function(){
34362 return this.adapter;
34366 * Set the adapter this SplitBar uses
34367 * @param {Object} adapter A SplitBar adapter object
34369 setAdapter : function(adapter){
34370 this.adapter = adapter;
34371 this.adapter.init(this);
34375 * Gets the minimum size for the resizing element
34376 * @return {Number} The minimum size
34378 getMinimumSize : function(){
34379 return this.minSize;
34383 * Sets the minimum size for the resizing element
34384 * @param {Number} minSize The minimum size
34386 setMinimumSize : function(minSize){
34387 this.minSize = minSize;
34391 * Gets the maximum size for the resizing element
34392 * @return {Number} The maximum size
34394 getMaximumSize : function(){
34395 return this.maxSize;
34399 * Sets the maximum size for the resizing element
34400 * @param {Number} maxSize The maximum size
34402 setMaximumSize : function(maxSize){
34403 this.maxSize = maxSize;
34407 * Sets the initialize size for the resizing element
34408 * @param {Number} size The initial size
34410 setCurrentSize : function(size){
34411 var oldAnimate = this.animate;
34412 this.animate = false;
34413 this.adapter.setElementSize(this, size);
34414 this.animate = oldAnimate;
34418 * Destroy this splitbar.
34419 * @param {Boolean} removeEl True to remove the element
34421 destroy : function(removeEl){
34423 this.shim.remove();
34426 this.proxy.parentNode.removeChild(this.proxy);
34434 * @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.
34436 Roo.bootstrap.SplitBar.createProxy = function(dir){
34437 var proxy = new Roo.Element(document.createElement("div"));
34438 proxy.unselectable();
34439 var cls = 'roo-splitbar-proxy';
34440 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34441 document.body.appendChild(proxy.dom);
34446 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34447 * Default Adapter. It assumes the splitter and resizing element are not positioned
34448 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34450 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34453 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34454 // do nothing for now
34455 init : function(s){
34459 * Called before drag operations to get the current size of the resizing element.
34460 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34462 getElementSize : function(s){
34463 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34464 return s.resizingEl.getWidth();
34466 return s.resizingEl.getHeight();
34471 * Called after drag operations to set the size of the resizing element.
34472 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34473 * @param {Number} newSize The new size to set
34474 * @param {Function} onComplete A function to be invoked when resizing is complete
34476 setElementSize : function(s, newSize, onComplete){
34477 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34479 s.resizingEl.setWidth(newSize);
34481 onComplete(s, newSize);
34484 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34489 s.resizingEl.setHeight(newSize);
34491 onComplete(s, newSize);
34494 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34501 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34502 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34503 * Adapter that moves the splitter element to align with the resized sizing element.
34504 * Used with an absolute positioned SplitBar.
34505 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34506 * document.body, make sure you assign an id to the body element.
34508 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34509 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34510 this.container = Roo.get(container);
34513 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34514 init : function(s){
34515 this.basic.init(s);
34518 getElementSize : function(s){
34519 return this.basic.getElementSize(s);
34522 setElementSize : function(s, newSize, onComplete){
34523 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34526 moveSplitter : function(s){
34527 var yes = Roo.bootstrap.SplitBar;
34528 switch(s.placement){
34530 s.el.setX(s.resizingEl.getRight());
34533 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34536 s.el.setY(s.resizingEl.getBottom());
34539 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34546 * Orientation constant - Create a vertical SplitBar
34550 Roo.bootstrap.SplitBar.VERTICAL = 1;
34553 * Orientation constant - Create a horizontal SplitBar
34557 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34560 * Placement constant - The resizing element is to the left of the splitter element
34564 Roo.bootstrap.SplitBar.LEFT = 1;
34567 * Placement constant - The resizing element is to the right of the splitter element
34571 Roo.bootstrap.SplitBar.RIGHT = 2;
34574 * Placement constant - The resizing element is positioned above the splitter element
34578 Roo.bootstrap.SplitBar.TOP = 3;
34581 * Placement constant - The resizing element is positioned under splitter element
34585 Roo.bootstrap.SplitBar.BOTTOM = 4;
34586 Roo.namespace("Roo.bootstrap.layout");/*
34588 * Ext JS Library 1.1.1
34589 * Copyright(c) 2006-2007, Ext JS, LLC.
34591 * Originally Released Under LGPL - original licence link has changed is not relivant.
34594 * <script type="text/javascript">
34598 * @class Roo.bootstrap.layout.Manager
34599 * @extends Roo.bootstrap.Component
34600 * Base class for layout managers.
34602 Roo.bootstrap.layout.Manager = function(config)
34604 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34610 /** false to disable window resize monitoring @type Boolean */
34611 this.monitorWindowResize = true;
34616 * Fires when a layout is performed.
34617 * @param {Roo.LayoutManager} this
34621 * @event regionresized
34622 * Fires when the user resizes a region.
34623 * @param {Roo.LayoutRegion} region The resized region
34624 * @param {Number} newSize The new size (width for east/west, height for north/south)
34626 "regionresized" : true,
34628 * @event regioncollapsed
34629 * Fires when a region is collapsed.
34630 * @param {Roo.LayoutRegion} region The collapsed region
34632 "regioncollapsed" : true,
34634 * @event regionexpanded
34635 * Fires when a region is expanded.
34636 * @param {Roo.LayoutRegion} region The expanded region
34638 "regionexpanded" : true
34640 this.updating = false;
34643 this.el = Roo.get(config.el);
34649 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34654 monitorWindowResize : true,
34660 onRender : function(ct, position)
34663 this.el = Roo.get(ct);
34666 //this.fireEvent('render',this);
34670 initEvents: function()
34674 // ie scrollbar fix
34675 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34676 document.body.scroll = "no";
34677 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34678 this.el.position('relative');
34680 this.id = this.el.id;
34681 this.el.addClass("roo-layout-container");
34682 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34683 if(this.el.dom != document.body ) {
34684 this.el.on('resize', this.layout,this);
34685 this.el.on('show', this.layout,this);
34691 * Returns true if this layout is currently being updated
34692 * @return {Boolean}
34694 isUpdating : function(){
34695 return this.updating;
34699 * Suspend the LayoutManager from doing auto-layouts while
34700 * making multiple add or remove calls
34702 beginUpdate : function(){
34703 this.updating = true;
34707 * Restore auto-layouts and optionally disable the manager from performing a layout
34708 * @param {Boolean} noLayout true to disable a layout update
34710 endUpdate : function(noLayout){
34711 this.updating = false;
34717 layout: function(){
34721 onRegionResized : function(region, newSize){
34722 this.fireEvent("regionresized", region, newSize);
34726 onRegionCollapsed : function(region){
34727 this.fireEvent("regioncollapsed", region);
34730 onRegionExpanded : function(region){
34731 this.fireEvent("regionexpanded", region);
34735 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34736 * performs box-model adjustments.
34737 * @return {Object} The size as an object {width: (the width), height: (the height)}
34739 getViewSize : function()
34742 if(this.el.dom != document.body){
34743 size = this.el.getSize();
34745 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34747 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34748 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34753 * Returns the Element this layout is bound to.
34754 * @return {Roo.Element}
34756 getEl : function(){
34761 * Returns the specified region.
34762 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34763 * @return {Roo.LayoutRegion}
34765 getRegion : function(target){
34766 return this.regions[target.toLowerCase()];
34769 onWindowResize : function(){
34770 if(this.monitorWindowResize){
34777 * Ext JS Library 1.1.1
34778 * Copyright(c) 2006-2007, Ext JS, LLC.
34780 * Originally Released Under LGPL - original licence link has changed is not relivant.
34783 * <script type="text/javascript">
34786 * @class Roo.bootstrap.layout.Border
34787 * @extends Roo.bootstrap.layout.Manager
34788 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34789 * please see: examples/bootstrap/nested.html<br><br>
34791 <b>The container the layout is rendered into can be either the body element or any other element.
34792 If it is not the body element, the container needs to either be an absolute positioned element,
34793 or you will need to add "position:relative" to the css of the container. You will also need to specify
34794 the container size if it is not the body element.</b>
34797 * Create a new Border
34798 * @param {Object} config Configuration options
34800 Roo.bootstrap.layout.Border = function(config){
34801 config = config || {};
34802 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34806 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34807 if(config[region]){
34808 config[region].region = region;
34809 this.addRegion(config[region]);
34815 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34817 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34819 * Creates and adds a new region if it doesn't already exist.
34820 * @param {String} target The target region key (north, south, east, west or center).
34821 * @param {Object} config The regions config object
34822 * @return {BorderLayoutRegion} The new region
34824 addRegion : function(config)
34826 if(!this.regions[config.region]){
34827 var r = this.factory(config);
34828 this.bindRegion(r);
34830 return this.regions[config.region];
34834 bindRegion : function(r){
34835 this.regions[r.config.region] = r;
34837 r.on("visibilitychange", this.layout, this);
34838 r.on("paneladded", this.layout, this);
34839 r.on("panelremoved", this.layout, this);
34840 r.on("invalidated", this.layout, this);
34841 r.on("resized", this.onRegionResized, this);
34842 r.on("collapsed", this.onRegionCollapsed, this);
34843 r.on("expanded", this.onRegionExpanded, this);
34847 * Performs a layout update.
34849 layout : function()
34851 if(this.updating) {
34855 // render all the rebions if they have not been done alreayd?
34856 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34857 if(this.regions[region] && !this.regions[region].bodyEl){
34858 this.regions[region].onRender(this.el)
34862 var size = this.getViewSize();
34863 var w = size.width;
34864 var h = size.height;
34869 //var x = 0, y = 0;
34871 var rs = this.regions;
34872 var north = rs["north"];
34873 var south = rs["south"];
34874 var west = rs["west"];
34875 var east = rs["east"];
34876 var center = rs["center"];
34877 //if(this.hideOnLayout){ // not supported anymore
34878 //c.el.setStyle("display", "none");
34880 if(north && north.isVisible()){
34881 var b = north.getBox();
34882 var m = north.getMargins();
34883 b.width = w - (m.left+m.right);
34886 centerY = b.height + b.y + m.bottom;
34887 centerH -= centerY;
34888 north.updateBox(this.safeBox(b));
34890 if(south && south.isVisible()){
34891 var b = south.getBox();
34892 var m = south.getMargins();
34893 b.width = w - (m.left+m.right);
34895 var totalHeight = (b.height + m.top + m.bottom);
34896 b.y = h - totalHeight + m.top;
34897 centerH -= totalHeight;
34898 south.updateBox(this.safeBox(b));
34900 if(west && west.isVisible()){
34901 var b = west.getBox();
34902 var m = west.getMargins();
34903 b.height = centerH - (m.top+m.bottom);
34905 b.y = centerY + m.top;
34906 var totalWidth = (b.width + m.left + m.right);
34907 centerX += totalWidth;
34908 centerW -= totalWidth;
34909 west.updateBox(this.safeBox(b));
34911 if(east && east.isVisible()){
34912 var b = east.getBox();
34913 var m = east.getMargins();
34914 b.height = centerH - (m.top+m.bottom);
34915 var totalWidth = (b.width + m.left + m.right);
34916 b.x = w - totalWidth + m.left;
34917 b.y = centerY + m.top;
34918 centerW -= totalWidth;
34919 east.updateBox(this.safeBox(b));
34922 var m = center.getMargins();
34924 x: centerX + m.left,
34925 y: centerY + m.top,
34926 width: centerW - (m.left+m.right),
34927 height: centerH - (m.top+m.bottom)
34929 //if(this.hideOnLayout){
34930 //center.el.setStyle("display", "block");
34932 center.updateBox(this.safeBox(centerBox));
34935 this.fireEvent("layout", this);
34939 safeBox : function(box){
34940 box.width = Math.max(0, box.width);
34941 box.height = Math.max(0, box.height);
34946 * Adds a ContentPanel (or subclass) to this layout.
34947 * @param {String} target The target region key (north, south, east, west or center).
34948 * @param {Roo.ContentPanel} panel The panel to add
34949 * @return {Roo.ContentPanel} The added panel
34951 add : function(target, panel){
34953 target = target.toLowerCase();
34954 return this.regions[target].add(panel);
34958 * Remove a ContentPanel (or subclass) to this layout.
34959 * @param {String} target The target region key (north, south, east, west or center).
34960 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34961 * @return {Roo.ContentPanel} The removed panel
34963 remove : function(target, panel){
34964 target = target.toLowerCase();
34965 return this.regions[target].remove(panel);
34969 * Searches all regions for a panel with the specified id
34970 * @param {String} panelId
34971 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34973 findPanel : function(panelId){
34974 var rs = this.regions;
34975 for(var target in rs){
34976 if(typeof rs[target] != "function"){
34977 var p = rs[target].getPanel(panelId);
34987 * Searches all regions for a panel with the specified id and activates (shows) it.
34988 * @param {String/ContentPanel} panelId The panels id or the panel itself
34989 * @return {Roo.ContentPanel} The shown panel or null
34991 showPanel : function(panelId) {
34992 var rs = this.regions;
34993 for(var target in rs){
34994 var r = rs[target];
34995 if(typeof r != "function"){
34996 if(r.hasPanel(panelId)){
34997 return r.showPanel(panelId);
35005 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35006 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35009 restoreState : function(provider){
35011 provider = Roo.state.Manager;
35013 var sm = new Roo.LayoutStateManager();
35014 sm.init(this, provider);
35020 * Adds a xtype elements to the layout.
35024 xtype : 'ContentPanel',
35031 xtype : 'NestedLayoutPanel',
35037 items : [ ... list of content panels or nested layout panels.. ]
35041 * @param {Object} cfg Xtype definition of item to add.
35043 addxtype : function(cfg)
35045 // basically accepts a pannel...
35046 // can accept a layout region..!?!?
35047 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35050 // theory? children can only be panels??
35052 //if (!cfg.xtype.match(/Panel$/)) {
35057 if (typeof(cfg.region) == 'undefined') {
35058 Roo.log("Failed to add Panel, region was not set");
35062 var region = cfg.region;
35068 xitems = cfg.items;
35075 case 'Content': // ContentPanel (el, cfg)
35076 case 'Scroll': // ContentPanel (el, cfg)
35078 cfg.autoCreate = true;
35079 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35081 // var el = this.el.createChild();
35082 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35085 this.add(region, ret);
35089 case 'TreePanel': // our new panel!
35090 cfg.el = this.el.createChild();
35091 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35092 this.add(region, ret);
35097 // create a new Layout (which is a Border Layout...
35099 var clayout = cfg.layout;
35100 clayout.el = this.el.createChild();
35101 clayout.items = clayout.items || [];
35105 // replace this exitems with the clayout ones..
35106 xitems = clayout.items;
35108 // force background off if it's in center...
35109 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35110 cfg.background = false;
35112 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35115 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35116 //console.log('adding nested layout panel ' + cfg.toSource());
35117 this.add(region, ret);
35118 nb = {}; /// find first...
35123 // needs grid and region
35125 //var el = this.getRegion(region).el.createChild();
35127 *var el = this.el.createChild();
35128 // create the grid first...
35129 cfg.grid.container = el;
35130 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35133 if (region == 'center' && this.active ) {
35134 cfg.background = false;
35137 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35139 this.add(region, ret);
35141 if (cfg.background) {
35142 // render grid on panel activation (if panel background)
35143 ret.on('activate', function(gp) {
35144 if (!gp.grid.rendered) {
35145 // gp.grid.render(el);
35149 // cfg.grid.render(el);
35155 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35156 // it was the old xcomponent building that caused this before.
35157 // espeically if border is the top element in the tree.
35167 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35169 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35170 this.add(region, ret);
35174 throw "Can not add '" + cfg.xtype + "' to Border";
35180 this.beginUpdate();
35184 Roo.each(xitems, function(i) {
35185 region = nb && i.region ? i.region : false;
35187 var add = ret.addxtype(i);
35190 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35191 if (!i.background) {
35192 abn[region] = nb[region] ;
35199 // make the last non-background panel active..
35200 //if (nb) { Roo.log(abn); }
35203 for(var r in abn) {
35204 region = this.getRegion(r);
35206 // tried using nb[r], but it does not work..
35208 region.showPanel(abn[r]);
35219 factory : function(cfg)
35222 var validRegions = Roo.bootstrap.layout.Border.regions;
35224 var target = cfg.region;
35227 var r = Roo.bootstrap.layout;
35231 return new r.North(cfg);
35233 return new r.South(cfg);
35235 return new r.East(cfg);
35237 return new r.West(cfg);
35239 return new r.Center(cfg);
35241 throw 'Layout region "'+target+'" not supported.';
35248 * Ext JS Library 1.1.1
35249 * Copyright(c) 2006-2007, Ext JS, LLC.
35251 * Originally Released Under LGPL - original licence link has changed is not relivant.
35254 * <script type="text/javascript">
35258 * @class Roo.bootstrap.layout.Basic
35259 * @extends Roo.util.Observable
35260 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35261 * and does not have a titlebar, tabs or any other features. All it does is size and position
35262 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35263 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35264 * @cfg {string} region the region that it inhabits..
35265 * @cfg {bool} skipConfig skip config?
35269 Roo.bootstrap.layout.Basic = function(config){
35271 this.mgr = config.mgr;
35273 this.position = config.region;
35275 var skipConfig = config.skipConfig;
35279 * @scope Roo.BasicLayoutRegion
35283 * @event beforeremove
35284 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35285 * @param {Roo.LayoutRegion} this
35286 * @param {Roo.ContentPanel} panel The panel
35287 * @param {Object} e The cancel event object
35289 "beforeremove" : true,
35291 * @event invalidated
35292 * Fires when the layout for this region is changed.
35293 * @param {Roo.LayoutRegion} this
35295 "invalidated" : true,
35297 * @event visibilitychange
35298 * Fires when this region is shown or hidden
35299 * @param {Roo.LayoutRegion} this
35300 * @param {Boolean} visibility true or false
35302 "visibilitychange" : true,
35304 * @event paneladded
35305 * Fires when a panel is added.
35306 * @param {Roo.LayoutRegion} this
35307 * @param {Roo.ContentPanel} panel The panel
35309 "paneladded" : true,
35311 * @event panelremoved
35312 * Fires when a panel is removed.
35313 * @param {Roo.LayoutRegion} this
35314 * @param {Roo.ContentPanel} panel The panel
35316 "panelremoved" : true,
35318 * @event beforecollapse
35319 * Fires when this region before collapse.
35320 * @param {Roo.LayoutRegion} this
35322 "beforecollapse" : true,
35325 * Fires when this region is collapsed.
35326 * @param {Roo.LayoutRegion} this
35328 "collapsed" : true,
35331 * Fires when this region is expanded.
35332 * @param {Roo.LayoutRegion} this
35337 * Fires when this region is slid into view.
35338 * @param {Roo.LayoutRegion} this
35340 "slideshow" : true,
35343 * Fires when this region slides out of view.
35344 * @param {Roo.LayoutRegion} this
35346 "slidehide" : true,
35348 * @event panelactivated
35349 * Fires when a panel is activated.
35350 * @param {Roo.LayoutRegion} this
35351 * @param {Roo.ContentPanel} panel The activated panel
35353 "panelactivated" : true,
35356 * Fires when the user resizes this region.
35357 * @param {Roo.LayoutRegion} this
35358 * @param {Number} newSize The new size (width for east/west, height for north/south)
35362 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35363 this.panels = new Roo.util.MixedCollection();
35364 this.panels.getKey = this.getPanelId.createDelegate(this);
35366 this.activePanel = null;
35367 // ensure listeners are added...
35369 if (config.listeners || config.events) {
35370 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35371 listeners : config.listeners || {},
35372 events : config.events || {}
35376 if(skipConfig !== true){
35377 this.applyConfig(config);
35381 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35383 getPanelId : function(p){
35387 applyConfig : function(config){
35388 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35389 this.config = config;
35394 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35395 * the width, for horizontal (north, south) the height.
35396 * @param {Number} newSize The new width or height
35398 resizeTo : function(newSize){
35399 var el = this.el ? this.el :
35400 (this.activePanel ? this.activePanel.getEl() : null);
35402 switch(this.position){
35405 el.setWidth(newSize);
35406 this.fireEvent("resized", this, newSize);
35410 el.setHeight(newSize);
35411 this.fireEvent("resized", this, newSize);
35417 getBox : function(){
35418 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35421 getMargins : function(){
35422 return this.margins;
35425 updateBox : function(box){
35427 var el = this.activePanel.getEl();
35428 el.dom.style.left = box.x + "px";
35429 el.dom.style.top = box.y + "px";
35430 this.activePanel.setSize(box.width, box.height);
35434 * Returns the container element for this region.
35435 * @return {Roo.Element}
35437 getEl : function(){
35438 return this.activePanel;
35442 * Returns true if this region is currently visible.
35443 * @return {Boolean}
35445 isVisible : function(){
35446 return this.activePanel ? true : false;
35449 setActivePanel : function(panel){
35450 panel = this.getPanel(panel);
35451 if(this.activePanel && this.activePanel != panel){
35452 this.activePanel.setActiveState(false);
35453 this.activePanel.getEl().setLeftTop(-10000,-10000);
35455 this.activePanel = panel;
35456 panel.setActiveState(true);
35458 panel.setSize(this.box.width, this.box.height);
35460 this.fireEvent("panelactivated", this, panel);
35461 this.fireEvent("invalidated");
35465 * Show the specified panel.
35466 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35467 * @return {Roo.ContentPanel} The shown panel or null
35469 showPanel : function(panel){
35470 panel = this.getPanel(panel);
35472 this.setActivePanel(panel);
35478 * Get the active panel for this region.
35479 * @return {Roo.ContentPanel} The active panel or null
35481 getActivePanel : function(){
35482 return this.activePanel;
35486 * Add the passed ContentPanel(s)
35487 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35488 * @return {Roo.ContentPanel} The panel added (if only one was added)
35490 add : function(panel){
35491 if(arguments.length > 1){
35492 for(var i = 0, len = arguments.length; i < len; i++) {
35493 this.add(arguments[i]);
35497 if(this.hasPanel(panel)){
35498 this.showPanel(panel);
35501 var el = panel.getEl();
35502 if(el.dom.parentNode != this.mgr.el.dom){
35503 this.mgr.el.dom.appendChild(el.dom);
35505 if(panel.setRegion){
35506 panel.setRegion(this);
35508 this.panels.add(panel);
35509 el.setStyle("position", "absolute");
35510 if(!panel.background){
35511 this.setActivePanel(panel);
35512 if(this.config.initialSize && this.panels.getCount()==1){
35513 this.resizeTo(this.config.initialSize);
35516 this.fireEvent("paneladded", this, panel);
35521 * Returns true if the panel is in this region.
35522 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35523 * @return {Boolean}
35525 hasPanel : function(panel){
35526 if(typeof panel == "object"){ // must be panel obj
35527 panel = panel.getId();
35529 return this.getPanel(panel) ? true : false;
35533 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35534 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35535 * @param {Boolean} preservePanel Overrides the config preservePanel option
35536 * @return {Roo.ContentPanel} The panel that was removed
35538 remove : function(panel, preservePanel){
35539 panel = this.getPanel(panel);
35544 this.fireEvent("beforeremove", this, panel, e);
35545 if(e.cancel === true){
35548 var panelId = panel.getId();
35549 this.panels.removeKey(panelId);
35554 * Returns the panel specified or null if it's not in this region.
35555 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35556 * @return {Roo.ContentPanel}
35558 getPanel : function(id){
35559 if(typeof id == "object"){ // must be panel obj
35562 return this.panels.get(id);
35566 * Returns this regions position (north/south/east/west/center).
35569 getPosition: function(){
35570 return this.position;
35574 * Ext JS Library 1.1.1
35575 * Copyright(c) 2006-2007, Ext JS, LLC.
35577 * Originally Released Under LGPL - original licence link has changed is not relivant.
35580 * <script type="text/javascript">
35584 * @class Roo.bootstrap.layout.Region
35585 * @extends Roo.bootstrap.layout.Basic
35586 * This class represents a region in a layout manager.
35588 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35589 * @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})
35590 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35591 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35592 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35593 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35594 * @cfg {String} title The title for the region (overrides panel titles)
35595 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35596 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35597 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35598 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35599 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35600 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35601 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35602 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35603 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35604 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35606 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35607 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35608 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35609 * @cfg {Number} width For East/West panels
35610 * @cfg {Number} height For North/South panels
35611 * @cfg {Boolean} split To show the splitter
35612 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35614 * @cfg {string} cls Extra CSS classes to add to region
35616 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35617 * @cfg {string} region the region that it inhabits..
35620 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35621 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35623 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35624 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35625 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35627 Roo.bootstrap.layout.Region = function(config)
35629 this.applyConfig(config);
35631 var mgr = config.mgr;
35632 var pos = config.region;
35633 config.skipConfig = true;
35634 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35637 this.onRender(mgr.el);
35640 this.visible = true;
35641 this.collapsed = false;
35642 this.unrendered_panels = [];
35645 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35647 position: '', // set by wrapper (eg. north/south etc..)
35648 unrendered_panels : null, // unrendered panels.
35649 createBody : function(){
35650 /** This region's body element
35651 * @type Roo.Element */
35652 this.bodyEl = this.el.createChild({
35654 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35658 onRender: function(ctr, pos)
35660 var dh = Roo.DomHelper;
35661 /** This region's container element
35662 * @type Roo.Element */
35663 this.el = dh.append(ctr.dom, {
35665 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35667 /** This region's title element
35668 * @type Roo.Element */
35670 this.titleEl = dh.append(this.el.dom,
35673 unselectable: "on",
35674 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35676 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35677 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35680 this.titleEl.enableDisplayMode();
35681 /** This region's title text element
35682 * @type HTMLElement */
35683 this.titleTextEl = this.titleEl.dom.firstChild;
35684 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35686 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35687 this.closeBtn.enableDisplayMode();
35688 this.closeBtn.on("click", this.closeClicked, this);
35689 this.closeBtn.hide();
35691 this.createBody(this.config);
35692 if(this.config.hideWhenEmpty){
35694 this.on("paneladded", this.validateVisibility, this);
35695 this.on("panelremoved", this.validateVisibility, this);
35697 if(this.autoScroll){
35698 this.bodyEl.setStyle("overflow", "auto");
35700 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35702 //if(c.titlebar !== false){
35703 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35704 this.titleEl.hide();
35706 this.titleEl.show();
35707 if(this.config.title){
35708 this.titleTextEl.innerHTML = this.config.title;
35712 if(this.config.collapsed){
35713 this.collapse(true);
35715 if(this.config.hidden){
35719 if (this.unrendered_panels && this.unrendered_panels.length) {
35720 for (var i =0;i< this.unrendered_panels.length; i++) {
35721 this.add(this.unrendered_panels[i]);
35723 this.unrendered_panels = null;
35729 applyConfig : function(c)
35732 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35733 var dh = Roo.DomHelper;
35734 if(c.titlebar !== false){
35735 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35736 this.collapseBtn.on("click", this.collapse, this);
35737 this.collapseBtn.enableDisplayMode();
35739 if(c.showPin === true || this.showPin){
35740 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35741 this.stickBtn.enableDisplayMode();
35742 this.stickBtn.on("click", this.expand, this);
35743 this.stickBtn.hide();
35748 /** This region's collapsed element
35749 * @type Roo.Element */
35752 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35753 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35756 if(c.floatable !== false){
35757 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35758 this.collapsedEl.on("click", this.collapseClick, this);
35761 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35762 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35763 id: "message", unselectable: "on", style:{"float":"left"}});
35764 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35766 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35767 this.expandBtn.on("click", this.expand, this);
35771 if(this.collapseBtn){
35772 this.collapseBtn.setVisible(c.collapsible == true);
35775 this.cmargins = c.cmargins || this.cmargins ||
35776 (this.position == "west" || this.position == "east" ?
35777 {top: 0, left: 2, right:2, bottom: 0} :
35778 {top: 2, left: 0, right:0, bottom: 2});
35780 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35783 this.bottomTabs = c.tabPosition != "top";
35785 this.autoScroll = c.autoScroll || false;
35790 this.duration = c.duration || .30;
35791 this.slideDuration = c.slideDuration || .45;
35796 * Returns true if this region is currently visible.
35797 * @return {Boolean}
35799 isVisible : function(){
35800 return this.visible;
35804 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35805 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35807 //setCollapsedTitle : function(title){
35808 // title = title || " ";
35809 // if(this.collapsedTitleTextEl){
35810 // this.collapsedTitleTextEl.innerHTML = title;
35814 getBox : function(){
35816 // if(!this.collapsed){
35817 b = this.el.getBox(false, true);
35819 // b = this.collapsedEl.getBox(false, true);
35824 getMargins : function(){
35825 return this.margins;
35826 //return this.collapsed ? this.cmargins : this.margins;
35829 highlight : function(){
35830 this.el.addClass("x-layout-panel-dragover");
35833 unhighlight : function(){
35834 this.el.removeClass("x-layout-panel-dragover");
35837 updateBox : function(box)
35839 if (!this.bodyEl) {
35840 return; // not rendered yet..
35844 if(!this.collapsed){
35845 this.el.dom.style.left = box.x + "px";
35846 this.el.dom.style.top = box.y + "px";
35847 this.updateBody(box.width, box.height);
35849 this.collapsedEl.dom.style.left = box.x + "px";
35850 this.collapsedEl.dom.style.top = box.y + "px";
35851 this.collapsedEl.setSize(box.width, box.height);
35854 this.tabs.autoSizeTabs();
35858 updateBody : function(w, h)
35861 this.el.setWidth(w);
35862 w -= this.el.getBorderWidth("rl");
35863 if(this.config.adjustments){
35864 w += this.config.adjustments[0];
35867 if(h !== null && h > 0){
35868 this.el.setHeight(h);
35869 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35870 h -= this.el.getBorderWidth("tb");
35871 if(this.config.adjustments){
35872 h += this.config.adjustments[1];
35874 this.bodyEl.setHeight(h);
35876 h = this.tabs.syncHeight(h);
35879 if(this.panelSize){
35880 w = w !== null ? w : this.panelSize.width;
35881 h = h !== null ? h : this.panelSize.height;
35883 if(this.activePanel){
35884 var el = this.activePanel.getEl();
35885 w = w !== null ? w : el.getWidth();
35886 h = h !== null ? h : el.getHeight();
35887 this.panelSize = {width: w, height: h};
35888 this.activePanel.setSize(w, h);
35890 if(Roo.isIE && this.tabs){
35891 this.tabs.el.repaint();
35896 * Returns the container element for this region.
35897 * @return {Roo.Element}
35899 getEl : function(){
35904 * Hides this region.
35907 //if(!this.collapsed){
35908 this.el.dom.style.left = "-2000px";
35911 // this.collapsedEl.dom.style.left = "-2000px";
35912 // this.collapsedEl.hide();
35914 this.visible = false;
35915 this.fireEvent("visibilitychange", this, false);
35919 * Shows this region if it was previously hidden.
35922 //if(!this.collapsed){
35925 // this.collapsedEl.show();
35927 this.visible = true;
35928 this.fireEvent("visibilitychange", this, true);
35931 closeClicked : function(){
35932 if(this.activePanel){
35933 this.remove(this.activePanel);
35937 collapseClick : function(e){
35939 e.stopPropagation();
35942 e.stopPropagation();
35948 * Collapses this region.
35949 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35952 collapse : function(skipAnim, skipCheck = false){
35953 if(this.collapsed) {
35957 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35959 this.collapsed = true;
35961 this.split.el.hide();
35963 if(this.config.animate && skipAnim !== true){
35964 this.fireEvent("invalidated", this);
35965 this.animateCollapse();
35967 this.el.setLocation(-20000,-20000);
35969 this.collapsedEl.show();
35970 this.fireEvent("collapsed", this);
35971 this.fireEvent("invalidated", this);
35977 animateCollapse : function(){
35982 * Expands this region if it was previously collapsed.
35983 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35984 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35987 expand : function(e, skipAnim){
35989 e.stopPropagation();
35991 if(!this.collapsed || this.el.hasActiveFx()) {
35995 this.afterSlideIn();
35998 this.collapsed = false;
35999 if(this.config.animate && skipAnim !== true){
36000 this.animateExpand();
36004 this.split.el.show();
36006 this.collapsedEl.setLocation(-2000,-2000);
36007 this.collapsedEl.hide();
36008 this.fireEvent("invalidated", this);
36009 this.fireEvent("expanded", this);
36013 animateExpand : function(){
36017 initTabs : function()
36019 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36021 var ts = new Roo.bootstrap.panel.Tabs({
36022 el: this.bodyEl.dom,
36023 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36024 disableTooltips: this.config.disableTabTips,
36025 toolbar : this.config.toolbar
36028 if(this.config.hideTabs){
36029 ts.stripWrap.setDisplayed(false);
36032 ts.resizeTabs = this.config.resizeTabs === true;
36033 ts.minTabWidth = this.config.minTabWidth || 40;
36034 ts.maxTabWidth = this.config.maxTabWidth || 250;
36035 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36036 ts.monitorResize = false;
36037 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36038 ts.bodyEl.addClass('roo-layout-tabs-body');
36039 this.panels.each(this.initPanelAsTab, this);
36042 initPanelAsTab : function(panel){
36043 var ti = this.tabs.addTab(
36047 this.config.closeOnTab && panel.isClosable(),
36050 if(panel.tabTip !== undefined){
36051 ti.setTooltip(panel.tabTip);
36053 ti.on("activate", function(){
36054 this.setActivePanel(panel);
36057 if(this.config.closeOnTab){
36058 ti.on("beforeclose", function(t, e){
36060 this.remove(panel);
36064 panel.tabItem = ti;
36069 updatePanelTitle : function(panel, title)
36071 if(this.activePanel == panel){
36072 this.updateTitle(title);
36075 var ti = this.tabs.getTab(panel.getEl().id);
36077 if(panel.tabTip !== undefined){
36078 ti.setTooltip(panel.tabTip);
36083 updateTitle : function(title){
36084 if(this.titleTextEl && !this.config.title){
36085 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36089 setActivePanel : function(panel)
36091 panel = this.getPanel(panel);
36092 if(this.activePanel && this.activePanel != panel){
36093 if(this.activePanel.setActiveState(false) === false){
36097 this.activePanel = panel;
36098 panel.setActiveState(true);
36099 if(this.panelSize){
36100 panel.setSize(this.panelSize.width, this.panelSize.height);
36103 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36105 this.updateTitle(panel.getTitle());
36107 this.fireEvent("invalidated", this);
36109 this.fireEvent("panelactivated", this, panel);
36113 * Shows the specified panel.
36114 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36115 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36117 showPanel : function(panel)
36119 panel = this.getPanel(panel);
36122 var tab = this.tabs.getTab(panel.getEl().id);
36123 if(tab.isHidden()){
36124 this.tabs.unhideTab(tab.id);
36128 this.setActivePanel(panel);
36135 * Get the active panel for this region.
36136 * @return {Roo.ContentPanel} The active panel or null
36138 getActivePanel : function(){
36139 return this.activePanel;
36142 validateVisibility : function(){
36143 if(this.panels.getCount() < 1){
36144 this.updateTitle(" ");
36145 this.closeBtn.hide();
36148 if(!this.isVisible()){
36155 * Adds the passed ContentPanel(s) to this region.
36156 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36157 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36159 add : function(panel)
36161 if(arguments.length > 1){
36162 for(var i = 0, len = arguments.length; i < len; i++) {
36163 this.add(arguments[i]);
36168 // if we have not been rendered yet, then we can not really do much of this..
36169 if (!this.bodyEl) {
36170 this.unrendered_panels.push(panel);
36177 if(this.hasPanel(panel)){
36178 this.showPanel(panel);
36181 panel.setRegion(this);
36182 this.panels.add(panel);
36183 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36184 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36185 // and hide them... ???
36186 this.bodyEl.dom.appendChild(panel.getEl().dom);
36187 if(panel.background !== true){
36188 this.setActivePanel(panel);
36190 this.fireEvent("paneladded", this, panel);
36197 this.initPanelAsTab(panel);
36201 if(panel.background !== true){
36202 this.tabs.activate(panel.getEl().id);
36204 this.fireEvent("paneladded", this, panel);
36209 * Hides the tab for the specified panel.
36210 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36212 hidePanel : function(panel){
36213 if(this.tabs && (panel = this.getPanel(panel))){
36214 this.tabs.hideTab(panel.getEl().id);
36219 * Unhides the tab for a previously hidden panel.
36220 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36222 unhidePanel : function(panel){
36223 if(this.tabs && (panel = this.getPanel(panel))){
36224 this.tabs.unhideTab(panel.getEl().id);
36228 clearPanels : function(){
36229 while(this.panels.getCount() > 0){
36230 this.remove(this.panels.first());
36235 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36236 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36237 * @param {Boolean} preservePanel Overrides the config preservePanel option
36238 * @return {Roo.ContentPanel} The panel that was removed
36240 remove : function(panel, preservePanel)
36242 panel = this.getPanel(panel);
36247 this.fireEvent("beforeremove", this, panel, e);
36248 if(e.cancel === true){
36251 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36252 var panelId = panel.getId();
36253 this.panels.removeKey(panelId);
36255 document.body.appendChild(panel.getEl().dom);
36258 this.tabs.removeTab(panel.getEl().id);
36259 }else if (!preservePanel){
36260 this.bodyEl.dom.removeChild(panel.getEl().dom);
36262 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36263 var p = this.panels.first();
36264 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36265 tempEl.appendChild(p.getEl().dom);
36266 this.bodyEl.update("");
36267 this.bodyEl.dom.appendChild(p.getEl().dom);
36269 this.updateTitle(p.getTitle());
36271 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36272 this.setActivePanel(p);
36274 panel.setRegion(null);
36275 if(this.activePanel == panel){
36276 this.activePanel = null;
36278 if(this.config.autoDestroy !== false && preservePanel !== true){
36279 try{panel.destroy();}catch(e){}
36281 this.fireEvent("panelremoved", this, panel);
36286 * Returns the TabPanel component used by this region
36287 * @return {Roo.TabPanel}
36289 getTabs : function(){
36293 createTool : function(parentEl, className){
36294 var btn = Roo.DomHelper.append(parentEl, {
36296 cls: "x-layout-tools-button",
36299 cls: "roo-layout-tools-button-inner " + className,
36303 btn.addClassOnOver("roo-layout-tools-button-over");
36308 * Ext JS Library 1.1.1
36309 * Copyright(c) 2006-2007, Ext JS, LLC.
36311 * Originally Released Under LGPL - original licence link has changed is not relivant.
36314 * <script type="text/javascript">
36320 * @class Roo.SplitLayoutRegion
36321 * @extends Roo.LayoutRegion
36322 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36324 Roo.bootstrap.layout.Split = function(config){
36325 this.cursor = config.cursor;
36326 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36329 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36331 splitTip : "Drag to resize.",
36332 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36333 useSplitTips : false,
36335 applyConfig : function(config){
36336 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36339 onRender : function(ctr,pos) {
36341 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36342 if(!this.config.split){
36347 var splitEl = Roo.DomHelper.append(ctr.dom, {
36349 id: this.el.id + "-split",
36350 cls: "roo-layout-split roo-layout-split-"+this.position,
36353 /** The SplitBar for this region
36354 * @type Roo.SplitBar */
36355 // does not exist yet...
36356 Roo.log([this.position, this.orientation]);
36358 this.split = new Roo.bootstrap.SplitBar({
36359 dragElement : splitEl,
36360 resizingElement: this.el,
36361 orientation : this.orientation
36364 this.split.on("moved", this.onSplitMove, this);
36365 this.split.useShim = this.config.useShim === true;
36366 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36367 if(this.useSplitTips){
36368 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36370 //if(config.collapsible){
36371 // this.split.el.on("dblclick", this.collapse, this);
36374 if(typeof this.config.minSize != "undefined"){
36375 this.split.minSize = this.config.minSize;
36377 if(typeof this.config.maxSize != "undefined"){
36378 this.split.maxSize = this.config.maxSize;
36380 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36381 this.hideSplitter();
36386 getHMaxSize : function(){
36387 var cmax = this.config.maxSize || 10000;
36388 var center = this.mgr.getRegion("center");
36389 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36392 getVMaxSize : function(){
36393 var cmax = this.config.maxSize || 10000;
36394 var center = this.mgr.getRegion("center");
36395 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36398 onSplitMove : function(split, newSize){
36399 this.fireEvent("resized", this, newSize);
36403 * Returns the {@link Roo.SplitBar} for this region.
36404 * @return {Roo.SplitBar}
36406 getSplitBar : function(){
36411 this.hideSplitter();
36412 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36415 hideSplitter : function(){
36417 this.split.el.setLocation(-2000,-2000);
36418 this.split.el.hide();
36424 this.split.el.show();
36426 Roo.bootstrap.layout.Split.superclass.show.call(this);
36429 beforeSlide: function(){
36430 if(Roo.isGecko){// firefox overflow auto bug workaround
36431 this.bodyEl.clip();
36433 this.tabs.bodyEl.clip();
36435 if(this.activePanel){
36436 this.activePanel.getEl().clip();
36438 if(this.activePanel.beforeSlide){
36439 this.activePanel.beforeSlide();
36445 afterSlide : function(){
36446 if(Roo.isGecko){// firefox overflow auto bug workaround
36447 this.bodyEl.unclip();
36449 this.tabs.bodyEl.unclip();
36451 if(this.activePanel){
36452 this.activePanel.getEl().unclip();
36453 if(this.activePanel.afterSlide){
36454 this.activePanel.afterSlide();
36460 initAutoHide : function(){
36461 if(this.autoHide !== false){
36462 if(!this.autoHideHd){
36463 var st = new Roo.util.DelayedTask(this.slideIn, this);
36464 this.autoHideHd = {
36465 "mouseout": function(e){
36466 if(!e.within(this.el, true)){
36470 "mouseover" : function(e){
36476 this.el.on(this.autoHideHd);
36480 clearAutoHide : function(){
36481 if(this.autoHide !== false){
36482 this.el.un("mouseout", this.autoHideHd.mouseout);
36483 this.el.un("mouseover", this.autoHideHd.mouseover);
36487 clearMonitor : function(){
36488 Roo.get(document).un("click", this.slideInIf, this);
36491 // these names are backwards but not changed for compat
36492 slideOut : function(){
36493 if(this.isSlid || this.el.hasActiveFx()){
36496 this.isSlid = true;
36497 if(this.collapseBtn){
36498 this.collapseBtn.hide();
36500 this.closeBtnState = this.closeBtn.getStyle('display');
36501 this.closeBtn.hide();
36503 this.stickBtn.show();
36506 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36507 this.beforeSlide();
36508 this.el.setStyle("z-index", 10001);
36509 this.el.slideIn(this.getSlideAnchor(), {
36510 callback: function(){
36512 this.initAutoHide();
36513 Roo.get(document).on("click", this.slideInIf, this);
36514 this.fireEvent("slideshow", this);
36521 afterSlideIn : function(){
36522 this.clearAutoHide();
36523 this.isSlid = false;
36524 this.clearMonitor();
36525 this.el.setStyle("z-index", "");
36526 if(this.collapseBtn){
36527 this.collapseBtn.show();
36529 this.closeBtn.setStyle('display', this.closeBtnState);
36531 this.stickBtn.hide();
36533 this.fireEvent("slidehide", this);
36536 slideIn : function(cb){
36537 if(!this.isSlid || this.el.hasActiveFx()){
36541 this.isSlid = false;
36542 this.beforeSlide();
36543 this.el.slideOut(this.getSlideAnchor(), {
36544 callback: function(){
36545 this.el.setLeftTop(-10000, -10000);
36547 this.afterSlideIn();
36555 slideInIf : function(e){
36556 if(!e.within(this.el)){
36561 animateCollapse : function(){
36562 this.beforeSlide();
36563 this.el.setStyle("z-index", 20000);
36564 var anchor = this.getSlideAnchor();
36565 this.el.slideOut(anchor, {
36566 callback : function(){
36567 this.el.setStyle("z-index", "");
36568 this.collapsedEl.slideIn(anchor, {duration:.3});
36570 this.el.setLocation(-10000,-10000);
36572 this.fireEvent("collapsed", this);
36579 animateExpand : function(){
36580 this.beforeSlide();
36581 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36582 this.el.setStyle("z-index", 20000);
36583 this.collapsedEl.hide({
36586 this.el.slideIn(this.getSlideAnchor(), {
36587 callback : function(){
36588 this.el.setStyle("z-index", "");
36591 this.split.el.show();
36593 this.fireEvent("invalidated", this);
36594 this.fireEvent("expanded", this);
36622 getAnchor : function(){
36623 return this.anchors[this.position];
36626 getCollapseAnchor : function(){
36627 return this.canchors[this.position];
36630 getSlideAnchor : function(){
36631 return this.sanchors[this.position];
36634 getAlignAdj : function(){
36635 var cm = this.cmargins;
36636 switch(this.position){
36652 getExpandAdj : function(){
36653 var c = this.collapsedEl, cm = this.cmargins;
36654 switch(this.position){
36656 return [-(cm.right+c.getWidth()+cm.left), 0];
36659 return [cm.right+c.getWidth()+cm.left, 0];
36662 return [0, -(cm.top+cm.bottom+c.getHeight())];
36665 return [0, cm.top+cm.bottom+c.getHeight()];
36671 * Ext JS Library 1.1.1
36672 * Copyright(c) 2006-2007, Ext JS, LLC.
36674 * Originally Released Under LGPL - original licence link has changed is not relivant.
36677 * <script type="text/javascript">
36680 * These classes are private internal classes
36682 Roo.bootstrap.layout.Center = function(config){
36683 config.region = "center";
36684 Roo.bootstrap.layout.Region.call(this, config);
36685 this.visible = true;
36686 this.minWidth = config.minWidth || 20;
36687 this.minHeight = config.minHeight || 20;
36690 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36692 // center panel can't be hidden
36696 // center panel can't be hidden
36699 getMinWidth: function(){
36700 return this.minWidth;
36703 getMinHeight: function(){
36704 return this.minHeight;
36717 Roo.bootstrap.layout.North = function(config)
36719 config.region = 'north';
36720 config.cursor = 'n-resize';
36722 Roo.bootstrap.layout.Split.call(this, config);
36726 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36727 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36728 this.split.el.addClass("roo-layout-split-v");
36730 var size = config.initialSize || config.height;
36731 if(typeof size != "undefined"){
36732 this.el.setHeight(size);
36735 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36737 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36741 getBox : function(){
36742 if(this.collapsed){
36743 return this.collapsedEl.getBox();
36745 var box = this.el.getBox();
36747 box.height += this.split.el.getHeight();
36752 updateBox : function(box){
36753 if(this.split && !this.collapsed){
36754 box.height -= this.split.el.getHeight();
36755 this.split.el.setLeft(box.x);
36756 this.split.el.setTop(box.y+box.height);
36757 this.split.el.setWidth(box.width);
36759 if(this.collapsed){
36760 this.updateBody(box.width, null);
36762 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36770 Roo.bootstrap.layout.South = function(config){
36771 config.region = 'south';
36772 config.cursor = 's-resize';
36773 Roo.bootstrap.layout.Split.call(this, config);
36775 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36776 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36777 this.split.el.addClass("roo-layout-split-v");
36779 var size = config.initialSize || config.height;
36780 if(typeof size != "undefined"){
36781 this.el.setHeight(size);
36785 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36786 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36787 getBox : function(){
36788 if(this.collapsed){
36789 return this.collapsedEl.getBox();
36791 var box = this.el.getBox();
36793 var sh = this.split.el.getHeight();
36800 updateBox : function(box){
36801 if(this.split && !this.collapsed){
36802 var sh = this.split.el.getHeight();
36805 this.split.el.setLeft(box.x);
36806 this.split.el.setTop(box.y-sh);
36807 this.split.el.setWidth(box.width);
36809 if(this.collapsed){
36810 this.updateBody(box.width, null);
36812 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36816 Roo.bootstrap.layout.East = function(config){
36817 config.region = "east";
36818 config.cursor = "e-resize";
36819 Roo.bootstrap.layout.Split.call(this, config);
36821 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36822 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36823 this.split.el.addClass("roo-layout-split-h");
36825 var size = config.initialSize || config.width;
36826 if(typeof size != "undefined"){
36827 this.el.setWidth(size);
36830 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36831 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36832 getBox : function(){
36833 if(this.collapsed){
36834 return this.collapsedEl.getBox();
36836 var box = this.el.getBox();
36838 var sw = this.split.el.getWidth();
36845 updateBox : function(box){
36846 if(this.split && !this.collapsed){
36847 var sw = this.split.el.getWidth();
36849 this.split.el.setLeft(box.x);
36850 this.split.el.setTop(box.y);
36851 this.split.el.setHeight(box.height);
36854 if(this.collapsed){
36855 this.updateBody(null, box.height);
36857 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36861 Roo.bootstrap.layout.West = function(config){
36862 config.region = "west";
36863 config.cursor = "w-resize";
36865 Roo.bootstrap.layout.Split.call(this, config);
36867 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36868 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36869 this.split.el.addClass("roo-layout-split-h");
36873 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36874 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36876 onRender: function(ctr, pos)
36878 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36879 var size = this.config.initialSize || this.config.width;
36880 if(typeof size != "undefined"){
36881 this.el.setWidth(size);
36885 getBox : function(){
36886 if(this.collapsed){
36887 return this.collapsedEl.getBox();
36889 var box = this.el.getBox();
36891 box.width += this.split.el.getWidth();
36896 updateBox : function(box){
36897 if(this.split && !this.collapsed){
36898 var sw = this.split.el.getWidth();
36900 this.split.el.setLeft(box.x+box.width);
36901 this.split.el.setTop(box.y);
36902 this.split.el.setHeight(box.height);
36904 if(this.collapsed){
36905 this.updateBody(null, box.height);
36907 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36910 Roo.namespace("Roo.bootstrap.panel");/*
36912 * Ext JS Library 1.1.1
36913 * Copyright(c) 2006-2007, Ext JS, LLC.
36915 * Originally Released Under LGPL - original licence link has changed is not relivant.
36918 * <script type="text/javascript">
36921 * @class Roo.ContentPanel
36922 * @extends Roo.util.Observable
36923 * A basic ContentPanel element.
36924 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36925 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36926 * @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
36927 * @cfg {Boolean} closable True if the panel can be closed/removed
36928 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36929 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36930 * @cfg {Toolbar} toolbar A toolbar for this panel
36931 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36932 * @cfg {String} title The title for this panel
36933 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36934 * @cfg {String} url Calls {@link #setUrl} with this value
36935 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36936 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36937 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36938 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36939 * @cfg {Boolean} badges render the badges
36942 * Create a new ContentPanel.
36943 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36944 * @param {String/Object} config A string to set only the title or a config object
36945 * @param {String} content (optional) Set the HTML content for this panel
36946 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36948 Roo.bootstrap.panel.Content = function( config){
36950 this.tpl = config.tpl || false;
36952 var el = config.el;
36953 var content = config.content;
36955 if(config.autoCreate){ // xtype is available if this is called from factory
36958 this.el = Roo.get(el);
36959 if(!this.el && config && config.autoCreate){
36960 if(typeof config.autoCreate == "object"){
36961 if(!config.autoCreate.id){
36962 config.autoCreate.id = config.id||el;
36964 this.el = Roo.DomHelper.append(document.body,
36965 config.autoCreate, true);
36967 var elcfg = { tag: "div",
36968 cls: "roo-layout-inactive-content",
36972 elcfg.html = config.html;
36976 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36979 this.closable = false;
36980 this.loaded = false;
36981 this.active = false;
36984 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36986 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36988 this.wrapEl = this.el; //this.el.wrap();
36990 if (config.toolbar.items) {
36991 ti = config.toolbar.items ;
36992 delete config.toolbar.items ;
36996 this.toolbar.render(this.wrapEl, 'before');
36997 for(var i =0;i < ti.length;i++) {
36998 // Roo.log(['add child', items[i]]);
36999 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37001 this.toolbar.items = nitems;
37002 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37003 delete config.toolbar;
37007 // xtype created footer. - not sure if will work as we normally have to render first..
37008 if (this.footer && !this.footer.el && this.footer.xtype) {
37009 if (!this.wrapEl) {
37010 this.wrapEl = this.el.wrap();
37013 this.footer.container = this.wrapEl.createChild();
37015 this.footer = Roo.factory(this.footer, Roo);
37020 if(typeof config == "string"){
37021 this.title = config;
37023 Roo.apply(this, config);
37027 this.resizeEl = Roo.get(this.resizeEl, true);
37029 this.resizeEl = this.el;
37031 // handle view.xtype
37039 * Fires when this panel is activated.
37040 * @param {Roo.ContentPanel} this
37044 * @event deactivate
37045 * Fires when this panel is activated.
37046 * @param {Roo.ContentPanel} this
37048 "deactivate" : true,
37052 * Fires when this panel is resized if fitToFrame is true.
37053 * @param {Roo.ContentPanel} this
37054 * @param {Number} width The width after any component adjustments
37055 * @param {Number} height The height after any component adjustments
37061 * Fires when this tab is created
37062 * @param {Roo.ContentPanel} this
37073 if(this.autoScroll){
37074 this.resizeEl.setStyle("overflow", "auto");
37076 // fix randome scrolling
37077 //this.el.on('scroll', function() {
37078 // Roo.log('fix random scolling');
37079 // this.scrollTo('top',0);
37082 content = content || this.content;
37084 this.setContent(content);
37086 if(config && config.url){
37087 this.setUrl(this.url, this.params, this.loadOnce);
37092 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37094 if (this.view && typeof(this.view.xtype) != 'undefined') {
37095 this.view.el = this.el.appendChild(document.createElement("div"));
37096 this.view = Roo.factory(this.view);
37097 this.view.render && this.view.render(false, '');
37101 this.fireEvent('render', this);
37104 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37108 setRegion : function(region){
37109 this.region = region;
37110 this.setActiveClass(region && !this.background);
37114 setActiveClass: function(state)
37117 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37118 this.el.setStyle('position','relative');
37120 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37121 this.el.setStyle('position', 'absolute');
37126 * Returns the toolbar for this Panel if one was configured.
37127 * @return {Roo.Toolbar}
37129 getToolbar : function(){
37130 return this.toolbar;
37133 setActiveState : function(active)
37135 this.active = active;
37136 this.setActiveClass(active);
37138 if(this.fireEvent("deactivate", this) === false){
37143 this.fireEvent("activate", this);
37147 * Updates this panel's element
37148 * @param {String} content The new content
37149 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37151 setContent : function(content, loadScripts){
37152 this.el.update(content, loadScripts);
37155 ignoreResize : function(w, h){
37156 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37159 this.lastSize = {width: w, height: h};
37164 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37165 * @return {Roo.UpdateManager} The UpdateManager
37167 getUpdateManager : function(){
37168 return this.el.getUpdateManager();
37171 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37172 * @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:
37175 url: "your-url.php",
37176 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37177 callback: yourFunction,
37178 scope: yourObject, //(optional scope)
37181 text: "Loading...",
37186 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37187 * 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.
37188 * @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}
37189 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37190 * @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.
37191 * @return {Roo.ContentPanel} this
37194 var um = this.el.getUpdateManager();
37195 um.update.apply(um, arguments);
37201 * 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.
37202 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37203 * @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)
37204 * @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)
37205 * @return {Roo.UpdateManager} The UpdateManager
37207 setUrl : function(url, params, loadOnce){
37208 if(this.refreshDelegate){
37209 this.removeListener("activate", this.refreshDelegate);
37211 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37212 this.on("activate", this.refreshDelegate);
37213 return this.el.getUpdateManager();
37216 _handleRefresh : function(url, params, loadOnce){
37217 if(!loadOnce || !this.loaded){
37218 var updater = this.el.getUpdateManager();
37219 updater.update(url, params, this._setLoaded.createDelegate(this));
37223 _setLoaded : function(){
37224 this.loaded = true;
37228 * Returns this panel's id
37231 getId : function(){
37236 * Returns this panel's element - used by regiosn to add.
37237 * @return {Roo.Element}
37239 getEl : function(){
37240 return this.wrapEl || this.el;
37245 adjustForComponents : function(width, height)
37247 //Roo.log('adjustForComponents ');
37248 if(this.resizeEl != this.el){
37249 width -= this.el.getFrameWidth('lr');
37250 height -= this.el.getFrameWidth('tb');
37253 var te = this.toolbar.getEl();
37254 te.setWidth(width);
37255 height -= te.getHeight();
37258 var te = this.footer.getEl();
37259 te.setWidth(width);
37260 height -= te.getHeight();
37264 if(this.adjustments){
37265 width += this.adjustments[0];
37266 height += this.adjustments[1];
37268 return {"width": width, "height": height};
37271 setSize : function(width, height){
37272 if(this.fitToFrame && !this.ignoreResize(width, height)){
37273 if(this.fitContainer && this.resizeEl != this.el){
37274 this.el.setSize(width, height);
37276 var size = this.adjustForComponents(width, height);
37277 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37278 this.fireEvent('resize', this, size.width, size.height);
37283 * Returns this panel's title
37286 getTitle : function(){
37288 if (typeof(this.title) != 'object') {
37293 for (var k in this.title) {
37294 if (!this.title.hasOwnProperty(k)) {
37298 if (k.indexOf('-') >= 0) {
37299 var s = k.split('-');
37300 for (var i = 0; i<s.length; i++) {
37301 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37304 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37311 * Set this panel's title
37312 * @param {String} title
37314 setTitle : function(title){
37315 this.title = title;
37317 this.region.updatePanelTitle(this, title);
37322 * Returns true is this panel was configured to be closable
37323 * @return {Boolean}
37325 isClosable : function(){
37326 return this.closable;
37329 beforeSlide : function(){
37331 this.resizeEl.clip();
37334 afterSlide : function(){
37336 this.resizeEl.unclip();
37340 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37341 * Will fail silently if the {@link #setUrl} method has not been called.
37342 * This does not activate the panel, just updates its content.
37344 refresh : function(){
37345 if(this.refreshDelegate){
37346 this.loaded = false;
37347 this.refreshDelegate();
37352 * Destroys this panel
37354 destroy : function(){
37355 this.el.removeAllListeners();
37356 var tempEl = document.createElement("span");
37357 tempEl.appendChild(this.el.dom);
37358 tempEl.innerHTML = "";
37364 * form - if the content panel contains a form - this is a reference to it.
37365 * @type {Roo.form.Form}
37369 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37370 * This contains a reference to it.
37376 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37386 * @param {Object} cfg Xtype definition of item to add.
37390 getChildContainer: function () {
37391 return this.getEl();
37396 var ret = new Roo.factory(cfg);
37401 if (cfg.xtype.match(/^Form$/)) {
37404 //if (this.footer) {
37405 // el = this.footer.container.insertSibling(false, 'before');
37407 el = this.el.createChild();
37410 this.form = new Roo.form.Form(cfg);
37413 if ( this.form.allItems.length) {
37414 this.form.render(el.dom);
37418 // should only have one of theses..
37419 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37420 // views.. should not be just added - used named prop 'view''
37422 cfg.el = this.el.appendChild(document.createElement("div"));
37425 var ret = new Roo.factory(cfg);
37427 ret.render && ret.render(false, ''); // render blank..
37437 * @class Roo.bootstrap.panel.Grid
37438 * @extends Roo.bootstrap.panel.Content
37440 * Create a new GridPanel.
37441 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37442 * @param {Object} config A the config object
37448 Roo.bootstrap.panel.Grid = function(config)
37452 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37453 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37455 config.el = this.wrapper;
37456 //this.el = this.wrapper;
37458 if (config.container) {
37459 // ctor'ed from a Border/panel.grid
37462 this.wrapper.setStyle("overflow", "hidden");
37463 this.wrapper.addClass('roo-grid-container');
37468 if(config.toolbar){
37469 var tool_el = this.wrapper.createChild();
37470 this.toolbar = Roo.factory(config.toolbar);
37472 if (config.toolbar.items) {
37473 ti = config.toolbar.items ;
37474 delete config.toolbar.items ;
37478 this.toolbar.render(tool_el);
37479 for(var i =0;i < ti.length;i++) {
37480 // Roo.log(['add child', items[i]]);
37481 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37483 this.toolbar.items = nitems;
37485 delete config.toolbar;
37488 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37489 config.grid.scrollBody = true;;
37490 config.grid.monitorWindowResize = false; // turn off autosizing
37491 config.grid.autoHeight = false;
37492 config.grid.autoWidth = false;
37494 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37496 if (config.background) {
37497 // render grid on panel activation (if panel background)
37498 this.on('activate', function(gp) {
37499 if (!gp.grid.rendered) {
37500 gp.grid.render(this.wrapper);
37501 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37506 this.grid.render(this.wrapper);
37507 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37510 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37511 // ??? needed ??? config.el = this.wrapper;
37516 // xtype created footer. - not sure if will work as we normally have to render first..
37517 if (this.footer && !this.footer.el && this.footer.xtype) {
37519 var ctr = this.grid.getView().getFooterPanel(true);
37520 this.footer.dataSource = this.grid.dataSource;
37521 this.footer = Roo.factory(this.footer, Roo);
37522 this.footer.render(ctr);
37532 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37533 getId : function(){
37534 return this.grid.id;
37538 * Returns the grid for this panel
37539 * @return {Roo.bootstrap.Table}
37541 getGrid : function(){
37545 setSize : function(width, height){
37546 if(!this.ignoreResize(width, height)){
37547 var grid = this.grid;
37548 var size = this.adjustForComponents(width, height);
37549 var gridel = grid.getGridEl();
37550 gridel.setSize(size.width, size.height);
37552 var thd = grid.getGridEl().select('thead',true).first();
37553 var tbd = grid.getGridEl().select('tbody', true).first();
37555 tbd.setSize(width, height - thd.getHeight());
37564 beforeSlide : function(){
37565 this.grid.getView().scroller.clip();
37568 afterSlide : function(){
37569 this.grid.getView().scroller.unclip();
37572 destroy : function(){
37573 this.grid.destroy();
37575 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37580 * @class Roo.bootstrap.panel.Nest
37581 * @extends Roo.bootstrap.panel.Content
37583 * Create a new Panel, that can contain a layout.Border.
37586 * @param {Roo.BorderLayout} layout The layout for this panel
37587 * @param {String/Object} config A string to set only the title or a config object
37589 Roo.bootstrap.panel.Nest = function(config)
37591 // construct with only one argument..
37592 /* FIXME - implement nicer consturctors
37593 if (layout.layout) {
37595 layout = config.layout;
37596 delete config.layout;
37598 if (layout.xtype && !layout.getEl) {
37599 // then layout needs constructing..
37600 layout = Roo.factory(layout, Roo);
37604 config.el = config.layout.getEl();
37606 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37608 config.layout.monitorWindowResize = false; // turn off autosizing
37609 this.layout = config.layout;
37610 this.layout.getEl().addClass("roo-layout-nested-layout");
37617 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37619 setSize : function(width, height){
37620 if(!this.ignoreResize(width, height)){
37621 var size = this.adjustForComponents(width, height);
37622 var el = this.layout.getEl();
37623 if (size.height < 1) {
37624 el.setWidth(size.width);
37626 el.setSize(size.width, size.height);
37628 var touch = el.dom.offsetWidth;
37629 this.layout.layout();
37630 // ie requires a double layout on the first pass
37631 if(Roo.isIE && !this.initialized){
37632 this.initialized = true;
37633 this.layout.layout();
37638 // activate all subpanels if not currently active..
37640 setActiveState : function(active){
37641 this.active = active;
37642 this.setActiveClass(active);
37645 this.fireEvent("deactivate", this);
37649 this.fireEvent("activate", this);
37650 // not sure if this should happen before or after..
37651 if (!this.layout) {
37652 return; // should not happen..
37655 for (var r in this.layout.regions) {
37656 reg = this.layout.getRegion(r);
37657 if (reg.getActivePanel()) {
37658 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37659 reg.setActivePanel(reg.getActivePanel());
37662 if (!reg.panels.length) {
37665 reg.showPanel(reg.getPanel(0));
37674 * Returns the nested BorderLayout for this panel
37675 * @return {Roo.BorderLayout}
37677 getLayout : function(){
37678 return this.layout;
37682 * Adds a xtype elements to the layout of the nested panel
37686 xtype : 'ContentPanel',
37693 xtype : 'NestedLayoutPanel',
37699 items : [ ... list of content panels or nested layout panels.. ]
37703 * @param {Object} cfg Xtype definition of item to add.
37705 addxtype : function(cfg) {
37706 return this.layout.addxtype(cfg);
37711 * Ext JS Library 1.1.1
37712 * Copyright(c) 2006-2007, Ext JS, LLC.
37714 * Originally Released Under LGPL - original licence link has changed is not relivant.
37717 * <script type="text/javascript">
37720 * @class Roo.TabPanel
37721 * @extends Roo.util.Observable
37722 * A lightweight tab container.
37726 // basic tabs 1, built from existing content
37727 var tabs = new Roo.TabPanel("tabs1");
37728 tabs.addTab("script", "View Script");
37729 tabs.addTab("markup", "View Markup");
37730 tabs.activate("script");
37732 // more advanced tabs, built from javascript
37733 var jtabs = new Roo.TabPanel("jtabs");
37734 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37736 // set up the UpdateManager
37737 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37738 var updater = tab2.getUpdateManager();
37739 updater.setDefaultUrl("ajax1.htm");
37740 tab2.on('activate', updater.refresh, updater, true);
37742 // Use setUrl for Ajax loading
37743 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37744 tab3.setUrl("ajax2.htm", null, true);
37747 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37750 jtabs.activate("jtabs-1");
37753 * Create a new TabPanel.
37754 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37755 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37757 Roo.bootstrap.panel.Tabs = function(config){
37759 * The container element for this TabPanel.
37760 * @type Roo.Element
37762 this.el = Roo.get(config.el);
37765 if(typeof config == "boolean"){
37766 this.tabPosition = config ? "bottom" : "top";
37768 Roo.apply(this, config);
37772 if(this.tabPosition == "bottom"){
37773 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37774 this.el.addClass("roo-tabs-bottom");
37776 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37777 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37778 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37780 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37782 if(this.tabPosition != "bottom"){
37783 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37784 * @type Roo.Element
37786 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37787 this.el.addClass("roo-tabs-top");
37791 this.bodyEl.setStyle("position", "relative");
37793 this.active = null;
37794 this.activateDelegate = this.activate.createDelegate(this);
37799 * Fires when the active tab changes
37800 * @param {Roo.TabPanel} this
37801 * @param {Roo.TabPanelItem} activePanel The new active tab
37805 * @event beforetabchange
37806 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37807 * @param {Roo.TabPanel} this
37808 * @param {Object} e Set cancel to true on this object to cancel the tab change
37809 * @param {Roo.TabPanelItem} tab The tab being changed to
37811 "beforetabchange" : true
37814 Roo.EventManager.onWindowResize(this.onResize, this);
37815 this.cpad = this.el.getPadding("lr");
37816 this.hiddenCount = 0;
37819 // toolbar on the tabbar support...
37820 if (this.toolbar) {
37821 alert("no toolbar support yet");
37822 this.toolbar = false;
37824 var tcfg = this.toolbar;
37825 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37826 this.toolbar = new Roo.Toolbar(tcfg);
37827 if (Roo.isSafari) {
37828 var tbl = tcfg.container.child('table', true);
37829 tbl.setAttribute('width', '100%');
37837 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37840 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37842 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37844 tabPosition : "top",
37846 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37848 currentTabWidth : 0,
37850 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37854 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37858 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37860 preferredTabWidth : 175,
37862 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37864 resizeTabs : false,
37866 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37868 monitorResize : true,
37870 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37875 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37876 * @param {String} id The id of the div to use <b>or create</b>
37877 * @param {String} text The text for the tab
37878 * @param {String} content (optional) Content to put in the TabPanelItem body
37879 * @param {Boolean} closable (optional) True to create a close icon on the tab
37880 * @return {Roo.TabPanelItem} The created TabPanelItem
37882 addTab : function(id, text, content, closable, tpl)
37884 var item = new Roo.bootstrap.panel.TabItem({
37888 closable : closable,
37891 this.addTabItem(item);
37893 item.setContent(content);
37899 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37900 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37901 * @return {Roo.TabPanelItem}
37903 getTab : function(id){
37904 return this.items[id];
37908 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37909 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37911 hideTab : function(id){
37912 var t = this.items[id];
37915 this.hiddenCount++;
37916 this.autoSizeTabs();
37921 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37922 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37924 unhideTab : function(id){
37925 var t = this.items[id];
37927 t.setHidden(false);
37928 this.hiddenCount--;
37929 this.autoSizeTabs();
37934 * Adds an existing {@link Roo.TabPanelItem}.
37935 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37937 addTabItem : function(item){
37938 this.items[item.id] = item;
37939 this.items.push(item);
37940 // if(this.resizeTabs){
37941 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37942 // this.autoSizeTabs();
37944 // item.autoSize();
37949 * Removes a {@link Roo.TabPanelItem}.
37950 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37952 removeTab : function(id){
37953 var items = this.items;
37954 var tab = items[id];
37955 if(!tab) { return; }
37956 var index = items.indexOf(tab);
37957 if(this.active == tab && items.length > 1){
37958 var newTab = this.getNextAvailable(index);
37963 this.stripEl.dom.removeChild(tab.pnode.dom);
37964 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37965 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37967 items.splice(index, 1);
37968 delete this.items[tab.id];
37969 tab.fireEvent("close", tab);
37970 tab.purgeListeners();
37971 this.autoSizeTabs();
37974 getNextAvailable : function(start){
37975 var items = this.items;
37977 // look for a next tab that will slide over to
37978 // replace the one being removed
37979 while(index < items.length){
37980 var item = items[++index];
37981 if(item && !item.isHidden()){
37985 // if one isn't found select the previous tab (on the left)
37988 var item = items[--index];
37989 if(item && !item.isHidden()){
37997 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37998 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38000 disableTab : function(id){
38001 var tab = this.items[id];
38002 if(tab && this.active != tab){
38008 * Enables a {@link Roo.TabPanelItem} that is disabled.
38009 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38011 enableTab : function(id){
38012 var tab = this.items[id];
38017 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38018 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38019 * @return {Roo.TabPanelItem} The TabPanelItem.
38021 activate : function(id){
38022 var tab = this.items[id];
38026 if(tab == this.active || tab.disabled){
38030 this.fireEvent("beforetabchange", this, e, tab);
38031 if(e.cancel !== true && !tab.disabled){
38033 this.active.hide();
38035 this.active = this.items[id];
38036 this.active.show();
38037 this.fireEvent("tabchange", this, this.active);
38043 * Gets the active {@link Roo.TabPanelItem}.
38044 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38046 getActiveTab : function(){
38047 return this.active;
38051 * Updates the tab body element to fit the height of the container element
38052 * for overflow scrolling
38053 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38055 syncHeight : function(targetHeight){
38056 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38057 var bm = this.bodyEl.getMargins();
38058 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38059 this.bodyEl.setHeight(newHeight);
38063 onResize : function(){
38064 if(this.monitorResize){
38065 this.autoSizeTabs();
38070 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38072 beginUpdate : function(){
38073 this.updating = true;
38077 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38079 endUpdate : function(){
38080 this.updating = false;
38081 this.autoSizeTabs();
38085 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38087 autoSizeTabs : function(){
38088 var count = this.items.length;
38089 var vcount = count - this.hiddenCount;
38090 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38093 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38094 var availWidth = Math.floor(w / vcount);
38095 var b = this.stripBody;
38096 if(b.getWidth() > w){
38097 var tabs = this.items;
38098 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38099 if(availWidth < this.minTabWidth){
38100 /*if(!this.sleft){ // incomplete scrolling code
38101 this.createScrollButtons();
38104 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38107 if(this.currentTabWidth < this.preferredTabWidth){
38108 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38114 * Returns the number of tabs in this TabPanel.
38117 getCount : function(){
38118 return this.items.length;
38122 * Resizes all the tabs to the passed width
38123 * @param {Number} The new width
38125 setTabWidth : function(width){
38126 this.currentTabWidth = width;
38127 for(var i = 0, len = this.items.length; i < len; i++) {
38128 if(!this.items[i].isHidden()) {
38129 this.items[i].setWidth(width);
38135 * Destroys this TabPanel
38136 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38138 destroy : function(removeEl){
38139 Roo.EventManager.removeResizeListener(this.onResize, this);
38140 for(var i = 0, len = this.items.length; i < len; i++){
38141 this.items[i].purgeListeners();
38143 if(removeEl === true){
38144 this.el.update("");
38149 createStrip : function(container)
38151 var strip = document.createElement("nav");
38152 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38153 container.appendChild(strip);
38157 createStripList : function(strip)
38159 // div wrapper for retard IE
38160 // returns the "tr" element.
38161 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38162 //'<div class="x-tabs-strip-wrap">'+
38163 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38164 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38165 return strip.firstChild; //.firstChild.firstChild.firstChild;
38167 createBody : function(container)
38169 var body = document.createElement("div");
38170 Roo.id(body, "tab-body");
38171 //Roo.fly(body).addClass("x-tabs-body");
38172 Roo.fly(body).addClass("tab-content");
38173 container.appendChild(body);
38176 createItemBody :function(bodyEl, id){
38177 var body = Roo.getDom(id);
38179 body = document.createElement("div");
38182 //Roo.fly(body).addClass("x-tabs-item-body");
38183 Roo.fly(body).addClass("tab-pane");
38184 bodyEl.insertBefore(body, bodyEl.firstChild);
38188 createStripElements : function(stripEl, text, closable, tpl)
38190 var td = document.createElement("li"); // was td..
38193 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38196 stripEl.appendChild(td);
38198 td.className = "x-tabs-closable";
38199 if(!this.closeTpl){
38200 this.closeTpl = new Roo.Template(
38201 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38202 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38203 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38206 var el = this.closeTpl.overwrite(td, {"text": text});
38207 var close = el.getElementsByTagName("div")[0];
38208 var inner = el.getElementsByTagName("em")[0];
38209 return {"el": el, "close": close, "inner": inner};
38212 // not sure what this is..
38213 // if(!this.tabTpl){
38214 //this.tabTpl = new Roo.Template(
38215 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38216 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38218 // this.tabTpl = new Roo.Template(
38219 // '<a href="#">' +
38220 // '<span unselectable="on"' +
38221 // (this.disableTooltips ? '' : ' title="{text}"') +
38222 // ' >{text}</span></a>'
38228 var template = tpl || this.tabTpl || false;
38232 template = new Roo.Template(
38234 '<span unselectable="on"' +
38235 (this.disableTooltips ? '' : ' title="{text}"') +
38236 ' >{text}</span></a>'
38240 switch (typeof(template)) {
38244 template = new Roo.Template(template);
38250 var el = template.overwrite(td, {"text": text});
38252 var inner = el.getElementsByTagName("span")[0];
38254 return {"el": el, "inner": inner};
38262 * @class Roo.TabPanelItem
38263 * @extends Roo.util.Observable
38264 * Represents an individual item (tab plus body) in a TabPanel.
38265 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38266 * @param {String} id The id of this TabPanelItem
38267 * @param {String} text The text for the tab of this TabPanelItem
38268 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38270 Roo.bootstrap.panel.TabItem = function(config){
38272 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38273 * @type Roo.TabPanel
38275 this.tabPanel = config.panel;
38277 * The id for this TabPanelItem
38280 this.id = config.id;
38282 this.disabled = false;
38284 this.text = config.text;
38286 this.loaded = false;
38287 this.closable = config.closable;
38290 * The body element for this TabPanelItem.
38291 * @type Roo.Element
38293 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38294 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38295 this.bodyEl.setStyle("display", "block");
38296 this.bodyEl.setStyle("zoom", "1");
38297 //this.hideAction();
38299 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38301 this.el = Roo.get(els.el);
38302 this.inner = Roo.get(els.inner, true);
38303 this.textEl = Roo.get(this.el.dom.firstChild, true);
38304 this.pnode = Roo.get(els.el.parentNode, true);
38305 // this.el.on("mousedown", this.onTabMouseDown, this);
38306 this.el.on("click", this.onTabClick, this);
38308 if(config.closable){
38309 var c = Roo.get(els.close, true);
38310 c.dom.title = this.closeText;
38311 c.addClassOnOver("close-over");
38312 c.on("click", this.closeClick, this);
38318 * Fires when this tab becomes the active tab.
38319 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38320 * @param {Roo.TabPanelItem} this
38324 * @event beforeclose
38325 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38326 * @param {Roo.TabPanelItem} this
38327 * @param {Object} e Set cancel to true on this object to cancel the close.
38329 "beforeclose": true,
38332 * Fires when this tab is closed.
38333 * @param {Roo.TabPanelItem} this
38337 * @event deactivate
38338 * Fires when this tab is no longer the active tab.
38339 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38340 * @param {Roo.TabPanelItem} this
38342 "deactivate" : true
38344 this.hidden = false;
38346 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38349 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38351 purgeListeners : function(){
38352 Roo.util.Observable.prototype.purgeListeners.call(this);
38353 this.el.removeAllListeners();
38356 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38359 this.pnode.addClass("active");
38362 this.tabPanel.stripWrap.repaint();
38364 this.fireEvent("activate", this.tabPanel, this);
38368 * Returns true if this tab is the active tab.
38369 * @return {Boolean}
38371 isActive : function(){
38372 return this.tabPanel.getActiveTab() == this;
38376 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38379 this.pnode.removeClass("active");
38381 this.fireEvent("deactivate", this.tabPanel, this);
38384 hideAction : function(){
38385 this.bodyEl.hide();
38386 this.bodyEl.setStyle("position", "absolute");
38387 this.bodyEl.setLeft("-20000px");
38388 this.bodyEl.setTop("-20000px");
38391 showAction : function(){
38392 this.bodyEl.setStyle("position", "relative");
38393 this.bodyEl.setTop("");
38394 this.bodyEl.setLeft("");
38395 this.bodyEl.show();
38399 * Set the tooltip for the tab.
38400 * @param {String} tooltip The tab's tooltip
38402 setTooltip : function(text){
38403 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38404 this.textEl.dom.qtip = text;
38405 this.textEl.dom.removeAttribute('title');
38407 this.textEl.dom.title = text;
38411 onTabClick : function(e){
38412 e.preventDefault();
38413 this.tabPanel.activate(this.id);
38416 onTabMouseDown : function(e){
38417 e.preventDefault();
38418 this.tabPanel.activate(this.id);
38421 getWidth : function(){
38422 return this.inner.getWidth();
38425 setWidth : function(width){
38426 var iwidth = width - this.pnode.getPadding("lr");
38427 this.inner.setWidth(iwidth);
38428 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38429 this.pnode.setWidth(width);
38433 * Show or hide the tab
38434 * @param {Boolean} hidden True to hide or false to show.
38436 setHidden : function(hidden){
38437 this.hidden = hidden;
38438 this.pnode.setStyle("display", hidden ? "none" : "");
38442 * Returns true if this tab is "hidden"
38443 * @return {Boolean}
38445 isHidden : function(){
38446 return this.hidden;
38450 * Returns the text for this tab
38453 getText : function(){
38457 autoSize : function(){
38458 //this.el.beginMeasure();
38459 this.textEl.setWidth(1);
38461 * #2804 [new] Tabs in Roojs
38462 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38464 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38465 //this.el.endMeasure();
38469 * Sets the text for the tab (Note: this also sets the tooltip text)
38470 * @param {String} text The tab's text and tooltip
38472 setText : function(text){
38474 this.textEl.update(text);
38475 this.setTooltip(text);
38476 //if(!this.tabPanel.resizeTabs){
38477 // this.autoSize();
38481 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38483 activate : function(){
38484 this.tabPanel.activate(this.id);
38488 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38490 disable : function(){
38491 if(this.tabPanel.active != this){
38492 this.disabled = true;
38493 this.pnode.addClass("disabled");
38498 * Enables this TabPanelItem if it was previously disabled.
38500 enable : function(){
38501 this.disabled = false;
38502 this.pnode.removeClass("disabled");
38506 * Sets the content for this TabPanelItem.
38507 * @param {String} content The content
38508 * @param {Boolean} loadScripts true to look for and load scripts
38510 setContent : function(content, loadScripts){
38511 this.bodyEl.update(content, loadScripts);
38515 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38516 * @return {Roo.UpdateManager} The UpdateManager
38518 getUpdateManager : function(){
38519 return this.bodyEl.getUpdateManager();
38523 * Set a URL to be used to load the content for this TabPanelItem.
38524 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38525 * @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)
38526 * @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)
38527 * @return {Roo.UpdateManager} The UpdateManager
38529 setUrl : function(url, params, loadOnce){
38530 if(this.refreshDelegate){
38531 this.un('activate', this.refreshDelegate);
38533 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38534 this.on("activate", this.refreshDelegate);
38535 return this.bodyEl.getUpdateManager();
38539 _handleRefresh : function(url, params, loadOnce){
38540 if(!loadOnce || !this.loaded){
38541 var updater = this.bodyEl.getUpdateManager();
38542 updater.update(url, params, this._setLoaded.createDelegate(this));
38547 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38548 * Will fail silently if the setUrl method has not been called.
38549 * This does not activate the panel, just updates its content.
38551 refresh : function(){
38552 if(this.refreshDelegate){
38553 this.loaded = false;
38554 this.refreshDelegate();
38559 _setLoaded : function(){
38560 this.loaded = true;
38564 closeClick : function(e){
38567 this.fireEvent("beforeclose", this, o);
38568 if(o.cancel !== true){
38569 this.tabPanel.removeTab(this.id);
38573 * The text displayed in the tooltip for the close icon.
38576 closeText : "Close this tab"
38579 * This script refer to:
38580 * Title: International Telephone Input
38581 * Author: Jack O'Connor
38582 * Code version: v12.1.12
38583 * Availability: https://github.com/jackocnr/intl-tel-input.git
38586 Roo.bootstrap.PhoneInputData = function() {
38589 "Afghanistan (افغانستان)",
38594 "Albania (Shqipëri)",
38599 "Algeria (الجزائر)",
38624 "Antigua and Barbuda",
38634 "Armenia (Հայաստան)",
38650 "Austria (Österreich)",
38655 "Azerbaijan (Azərbaycan)",
38665 "Bahrain (البحرين)",
38670 "Bangladesh (বাংলাদেশ)",
38680 "Belarus (Беларусь)",
38685 "Belgium (België)",
38715 "Bosnia and Herzegovina (Босна и Херцеговина)",
38730 "British Indian Ocean Territory",
38735 "British Virgin Islands",
38745 "Bulgaria (България)",
38755 "Burundi (Uburundi)",
38760 "Cambodia (កម្ពុជា)",
38765 "Cameroon (Cameroun)",
38774 ["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"]
38777 "Cape Verde (Kabu Verdi)",
38782 "Caribbean Netherlands",
38793 "Central African Republic (République centrafricaine)",
38813 "Christmas Island",
38819 "Cocos (Keeling) Islands",
38830 "Comoros (جزر القمر)",
38835 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38840 "Congo (Republic) (Congo-Brazzaville)",
38860 "Croatia (Hrvatska)",
38881 "Czech Republic (Česká republika)",
38886 "Denmark (Danmark)",
38901 "Dominican Republic (República Dominicana)",
38905 ["809", "829", "849"]
38923 "Equatorial Guinea (Guinea Ecuatorial)",
38943 "Falkland Islands (Islas Malvinas)",
38948 "Faroe Islands (Føroyar)",
38969 "French Guiana (Guyane française)",
38974 "French Polynesia (Polynésie française)",
38989 "Georgia (საქართველო)",
38994 "Germany (Deutschland)",
39014 "Greenland (Kalaallit Nunaat)",
39051 "Guinea-Bissau (Guiné Bissau)",
39076 "Hungary (Magyarország)",
39081 "Iceland (Ísland)",
39101 "Iraq (العراق)",
39117 "Israel (ישראל)",
39144 "Jordan (الأردن)",
39149 "Kazakhstan (Казахстан)",
39170 "Kuwait (الكويت)",
39175 "Kyrgyzstan (Кыргызстан)",
39185 "Latvia (Latvija)",
39190 "Lebanon (لبنان)",
39205 "Libya (ليبيا)",
39215 "Lithuania (Lietuva)",
39230 "Macedonia (FYROM) (Македонија)",
39235 "Madagascar (Madagasikara)",
39265 "Marshall Islands",
39275 "Mauritania (موريتانيا)",
39280 "Mauritius (Moris)",
39301 "Moldova (Republica Moldova)",
39311 "Mongolia (Монгол)",
39316 "Montenegro (Crna Gora)",
39326 "Morocco (المغرب)",
39332 "Mozambique (Moçambique)",
39337 "Myanmar (Burma) (မြန်မာ)",
39342 "Namibia (Namibië)",
39357 "Netherlands (Nederland)",
39362 "New Caledonia (Nouvelle-Calédonie)",
39397 "North Korea (조선 민주주의 인민 공화국)",
39402 "Northern Mariana Islands",
39418 "Pakistan (پاکستان)",
39428 "Palestine (فلسطين)",
39438 "Papua New Guinea",
39480 "Réunion (La Réunion)",
39486 "Romania (România)",
39502 "Saint Barthélemy",
39513 "Saint Kitts and Nevis",
39523 "Saint Martin (Saint-Martin (partie française))",
39529 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39534 "Saint Vincent and the Grenadines",
39549 "São Tomé and Príncipe (São Tomé e Príncipe)",
39554 "Saudi Arabia (المملكة العربية السعودية)",
39559 "Senegal (Sénégal)",
39589 "Slovakia (Slovensko)",
39594 "Slovenia (Slovenija)",
39604 "Somalia (Soomaaliya)",
39614 "South Korea (대한민국)",
39619 "South Sudan (جنوب السودان)",
39629 "Sri Lanka (ශ්රී ලංකාව)",
39634 "Sudan (السودان)",
39644 "Svalbard and Jan Mayen",
39655 "Sweden (Sverige)",
39660 "Switzerland (Schweiz)",
39665 "Syria (سوريا)",
39710 "Trinidad and Tobago",
39715 "Tunisia (تونس)",
39720 "Turkey (Türkiye)",
39730 "Turks and Caicos Islands",
39740 "U.S. Virgin Islands",
39750 "Ukraine (Україна)",
39755 "United Arab Emirates (الإمارات العربية المتحدة)",
39777 "Uzbekistan (Oʻzbekiston)",
39787 "Vatican City (Città del Vaticano)",
39798 "Vietnam (Việt Nam)",
39803 "Wallis and Futuna (Wallis-et-Futuna)",
39808 "Western Sahara (الصحراء الغربية)",
39814 "Yemen (اليمن)",
39838 * This script refer to:
39839 * Title: International Telephone Input
39840 * Author: Jack O'Connor
39841 * Code version: v12.1.12
39842 * Availability: https://github.com/jackocnr/intl-tel-input.git
39846 * @class Roo.bootstrap.PhoneInput
39847 * @extends Roo.bootstrap.TriggerField
39848 * An input with International dial-code selection
39850 * @cfg {String} defaultDialCode default '+852'
39851 * @cfg {Array} preferedCountries default []
39854 * Create a new PhoneInput.
39855 * @param {Object} config Configuration options
39858 Roo.bootstrap.PhoneInput = function(config) {
39859 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39862 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39864 listWidth: undefined,
39866 selectedClass: 'active',
39868 invalidClass : "has-warning",
39870 validClass: 'has-success',
39872 allowed: '0123456789',
39875 * @cfg {String} defaultDialCode The default dial code when initializing the input
39877 defaultDialCode: '+852',
39880 * @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
39882 preferedCountries: false,
39884 getAutoCreate : function()
39886 var data = Roo.bootstrap.PhoneInputData();
39887 var align = this.labelAlign || this.parentLabelAlign();
39890 this.allCountries = [];
39891 this.dialCodeMapping = [];
39893 for (var i = 0; i < data.length; i++) {
39895 this.allCountries[i] = {
39899 priority: c[3] || 0,
39900 areaCodes: c[4] || null
39902 this.dialCodeMapping[c[2]] = {
39905 priority: c[3] || 0,
39906 areaCodes: c[4] || null
39918 cls : 'form-control tel-input',
39919 autocomplete: 'new-password'
39922 var hiddenInput = {
39925 cls: 'hidden-tel-input'
39929 hiddenInput.name = this.name;
39932 if (this.disabled) {
39933 input.disabled = true;
39936 var flag_container = {
39953 cls: this.hasFeedback ? 'has-feedback' : '',
39959 cls: 'dial-code-holder',
39966 cls: 'roo-select2-container input-group',
39973 if (this.fieldLabel.length) {
39976 tooltip: 'This field is required'
39982 cls: 'control-label',
39988 html: this.fieldLabel
39991 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39997 if(this.indicatorpos == 'right') {
39998 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40005 if(align == 'left') {
40013 if(this.labelWidth > 12){
40014 label.style = "width: " + this.labelWidth + 'px';
40016 if(this.labelWidth < 13 && this.labelmd == 0){
40017 this.labelmd = this.labelWidth;
40019 if(this.labellg > 0){
40020 label.cls += ' col-lg-' + this.labellg;
40021 input.cls += ' col-lg-' + (12 - this.labellg);
40023 if(this.labelmd > 0){
40024 label.cls += ' col-md-' + this.labelmd;
40025 container.cls += ' col-md-' + (12 - this.labelmd);
40027 if(this.labelsm > 0){
40028 label.cls += ' col-sm-' + this.labelsm;
40029 container.cls += ' col-sm-' + (12 - this.labelsm);
40031 if(this.labelxs > 0){
40032 label.cls += ' col-xs-' + this.labelxs;
40033 container.cls += ' col-xs-' + (12 - this.labelxs);
40043 var settings = this;
40045 ['xs','sm','md','lg'].map(function(size){
40046 if (settings[size]) {
40047 cfg.cls += ' col-' + size + '-' + settings[size];
40051 this.store = new Roo.data.Store({
40052 proxy : new Roo.data.MemoryProxy({}),
40053 reader : new Roo.data.JsonReader({
40064 'name' : 'dialCode',
40068 'name' : 'priority',
40072 'name' : 'areaCodes',
40079 if(!this.preferedCountries) {
40080 this.preferedCountries = [
40087 var p = this.preferedCountries.reverse();
40090 for (var i = 0; i < p.length; i++) {
40091 for (var j = 0; j < this.allCountries.length; j++) {
40092 if(this.allCountries[j].iso2 == p[i]) {
40093 var t = this.allCountries[j];
40094 this.allCountries.splice(j,1);
40095 this.allCountries.unshift(t);
40101 this.store.proxy.data = {
40103 data: this.allCountries
40109 initEvents : function()
40112 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40114 this.indicator = this.indicatorEl();
40115 this.flag = this.flagEl();
40116 this.dialCodeHolder = this.dialCodeHolderEl();
40118 this.trigger = this.el.select('div.flag-box',true).first();
40119 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40124 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40125 _this.list.setWidth(lw);
40128 this.list.on('mouseover', this.onViewOver, this);
40129 this.list.on('mousemove', this.onViewMove, this);
40130 this.inputEl().on("keyup", this.onKeyUp, this);
40132 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40134 this.view = new Roo.View(this.list, this.tpl, {
40135 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40138 this.view.on('click', this.onViewClick, this);
40139 this.setValue(this.defaultDialCode);
40142 onTriggerClick : function(e)
40144 Roo.log('trigger click');
40149 if(this.isExpanded()){
40151 this.hasFocus = false;
40153 this.store.load({});
40154 this.hasFocus = true;
40159 isExpanded : function()
40161 return this.list.isVisible();
40164 collapse : function()
40166 if(!this.isExpanded()){
40170 Roo.get(document).un('mousedown', this.collapseIf, this);
40171 Roo.get(document).un('mousewheel', this.collapseIf, this);
40172 this.fireEvent('collapse', this);
40176 expand : function()
40180 if(this.isExpanded() || !this.hasFocus){
40184 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40185 this.list.setWidth(lw);
40188 this.restrictHeight();
40190 Roo.get(document).on('mousedown', this.collapseIf, this);
40191 Roo.get(document).on('mousewheel', this.collapseIf, this);
40193 this.fireEvent('expand', this);
40196 restrictHeight : function()
40198 this.list.alignTo(this.inputEl(), this.listAlign);
40199 this.list.alignTo(this.inputEl(), this.listAlign);
40202 onViewOver : function(e, t)
40204 if(this.inKeyMode){
40207 var item = this.view.findItemFromChild(t);
40210 var index = this.view.indexOf(item);
40211 this.select(index, false);
40216 onViewClick : function(view, doFocus, el, e)
40218 var index = this.view.getSelectedIndexes()[0];
40220 var r = this.store.getAt(index);
40223 this.onSelect(r, index);
40225 if(doFocus !== false && !this.blockFocus){
40226 this.inputEl().focus();
40230 onViewMove : function(e, t)
40232 this.inKeyMode = false;
40235 select : function(index, scrollIntoView)
40237 this.selectedIndex = index;
40238 this.view.select(index);
40239 if(scrollIntoView !== false){
40240 var el = this.view.getNode(index);
40242 this.list.scrollChildIntoView(el, false);
40247 createList : function()
40249 this.list = Roo.get(document.body).createChild({
40251 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40252 style: 'display:none'
40255 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40258 collapseIf : function(e)
40260 var in_combo = e.within(this.el);
40261 var in_list = e.within(this.list);
40262 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40264 if (in_combo || in_list || is_list) {
40270 onSelect : function(record, index)
40272 if(this.fireEvent('beforeselect', this, record, index) !== false){
40274 this.setFlagClass(record.data.iso2);
40275 this.setDialCode(record.data.dialCode);
40276 this.hasFocus = false;
40278 this.fireEvent('select', this, record, index);
40282 flagEl : function()
40284 var flag = this.el.select('div.flag',true).first();
40291 dialCodeHolderEl : function()
40293 var d = this.el.select('input.dial-code-holder',true).first();
40300 setDialCode : function(v)
40302 this.dialCodeHolder.dom.value = '+'+v;
40305 setFlagClass : function(n)
40307 this.flag.dom.className = 'flag '+n;
40310 getValue : function()
40312 var v = this.inputEl().getValue();
40313 if(this.dialCodeHolder) {
40314 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40319 setValue : function(v)
40321 var d = this.getDialCode(v);
40323 //invalid dial code
40324 if(v.length == 0 || !d || d.length == 0) {
40326 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40327 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40333 this.setFlagClass(this.dialCodeMapping[d].iso2);
40334 this.setDialCode(d);
40335 this.inputEl().dom.value = v.replace('+'+d,'');
40336 this.hiddenEl().dom.value = this.getValue();
40341 getDialCode : function(v)
40345 if (v.length == 0) {
40346 return this.dialCodeHolder.dom.value;
40350 if (v.charAt(0) != "+") {
40353 var numericChars = "";
40354 for (var i = 1; i < v.length; i++) {
40355 var c = v.charAt(i);
40358 if (this.dialCodeMapping[numericChars]) {
40359 dialCode = v.substr(1, i);
40361 if (numericChars.length == 4) {
40371 this.setValue(this.defaultDialCode);
40375 hiddenEl : function()
40377 return this.el.select('input.hidden-tel-input',true).first();
40380 onKeyUp : function(e){
40382 var k = e.getKey();
40383 var c = e.getCharCode();
40386 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40387 this.allowed.indexOf(String.fromCharCode(c)) === -1
40392 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40395 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40399 this.setValue(this.getValue());
40404 * @class Roo.bootstrap.MoneyField
40405 * @extends Roo.bootstrap.ComboBox
40406 * Bootstrap MoneyField class
40409 * Create a new MoneyField.
40410 * @param {Object} config Configuration options
40413 Roo.bootstrap.MoneyField = function(config) {
40415 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40419 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40422 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40424 allowDecimals : true,
40426 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40428 decimalSeparator : ".",
40430 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40432 decimalPrecision : 0,
40434 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40436 allowNegative : true,
40438 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40442 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40444 minValue : Number.NEGATIVE_INFINITY,
40446 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40448 maxValue : Number.MAX_VALUE,
40450 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40452 minText : "The minimum value for this field is {0}",
40454 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40456 maxText : "The maximum value for this field is {0}",
40458 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40459 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40461 nanText : "{0} is not a valid number",
40463 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40467 * @cfg {String} defaults currency of the MoneyField
40468 * value should be in lkey
40470 defaultCurrency : false,
40472 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40474 thousandsDelimiter : false,
40484 getAutoCreate : function()
40486 var align = this.labelAlign || this.parentLabelAlign();
40498 cls : 'form-control roo-money-amount-input',
40499 autocomplete: 'new-password'
40502 var hiddenInput = {
40506 cls: 'hidden-number-input'
40510 hiddenInput.name = this.name;
40513 if (this.disabled) {
40514 input.disabled = true;
40517 var clg = 12 - this.inputlg;
40518 var cmd = 12 - this.inputmd;
40519 var csm = 12 - this.inputsm;
40520 var cxs = 12 - this.inputxs;
40524 cls : 'row roo-money-field',
40528 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40532 cls: 'roo-select2-container input-group',
40536 cls : 'form-control roo-money-currency-input',
40537 autocomplete: 'new-password',
40539 name : this.currencyName
40543 cls : 'input-group-addon',
40557 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40561 cls: this.hasFeedback ? 'has-feedback' : '',
40572 if (this.fieldLabel.length) {
40575 tooltip: 'This field is required'
40581 cls: 'control-label',
40587 html: this.fieldLabel
40590 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40596 if(this.indicatorpos == 'right') {
40597 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40604 if(align == 'left') {
40612 if(this.labelWidth > 12){
40613 label.style = "width: " + this.labelWidth + 'px';
40615 if(this.labelWidth < 13 && this.labelmd == 0){
40616 this.labelmd = this.labelWidth;
40618 if(this.labellg > 0){
40619 label.cls += ' col-lg-' + this.labellg;
40620 input.cls += ' col-lg-' + (12 - this.labellg);
40622 if(this.labelmd > 0){
40623 label.cls += ' col-md-' + this.labelmd;
40624 container.cls += ' col-md-' + (12 - this.labelmd);
40626 if(this.labelsm > 0){
40627 label.cls += ' col-sm-' + this.labelsm;
40628 container.cls += ' col-sm-' + (12 - this.labelsm);
40630 if(this.labelxs > 0){
40631 label.cls += ' col-xs-' + this.labelxs;
40632 container.cls += ' col-xs-' + (12 - this.labelxs);
40643 var settings = this;
40645 ['xs','sm','md','lg'].map(function(size){
40646 if (settings[size]) {
40647 cfg.cls += ' col-' + size + '-' + settings[size];
40654 initEvents : function()
40656 this.indicator = this.indicatorEl();
40658 this.initCurrencyEvent();
40660 this.initNumberEvent();
40663 initCurrencyEvent : function()
40666 throw "can not find store for combo";
40669 this.store = Roo.factory(this.store, Roo.data);
40670 this.store.parent = this;
40674 this.triggerEl = this.el.select('.input-group-addon', true).first();
40676 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40681 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40682 _this.list.setWidth(lw);
40685 this.list.on('mouseover', this.onViewOver, this);
40686 this.list.on('mousemove', this.onViewMove, this);
40687 this.list.on('scroll', this.onViewScroll, this);
40690 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40693 this.view = new Roo.View(this.list, this.tpl, {
40694 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40697 this.view.on('click', this.onViewClick, this);
40699 this.store.on('beforeload', this.onBeforeLoad, this);
40700 this.store.on('load', this.onLoad, this);
40701 this.store.on('loadexception', this.onLoadException, this);
40703 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40704 "up" : function(e){
40705 this.inKeyMode = true;
40709 "down" : function(e){
40710 if(!this.isExpanded()){
40711 this.onTriggerClick();
40713 this.inKeyMode = true;
40718 "enter" : function(e){
40721 if(this.fireEvent("specialkey", this, e)){
40722 this.onViewClick(false);
40728 "esc" : function(e){
40732 "tab" : function(e){
40735 if(this.fireEvent("specialkey", this, e)){
40736 this.onViewClick(false);
40744 doRelay : function(foo, bar, hname){
40745 if(hname == 'down' || this.scope.isExpanded()){
40746 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40754 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40758 initNumberEvent : function(e)
40760 this.inputEl().on("keydown" , this.fireKey, this);
40761 this.inputEl().on("focus", this.onFocus, this);
40762 this.inputEl().on("blur", this.onBlur, this);
40764 this.inputEl().relayEvent('keyup', this);
40766 if(this.indicator){
40767 this.indicator.addClass('invisible');
40770 this.originalValue = this.getValue();
40772 if(this.validationEvent == 'keyup'){
40773 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40774 this.inputEl().on('keyup', this.filterValidation, this);
40776 else if(this.validationEvent !== false){
40777 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40780 if(this.selectOnFocus){
40781 this.on("focus", this.preFocus, this);
40784 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40785 this.inputEl().on("keypress", this.filterKeys, this);
40787 this.inputEl().relayEvent('keypress', this);
40790 var allowed = "0123456789";
40792 if(this.allowDecimals){
40793 allowed += this.decimalSeparator;
40796 if(this.allowNegative){
40800 if(this.thousandsDelimiter) {
40804 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40806 var keyPress = function(e){
40808 var k = e.getKey();
40810 var c = e.getCharCode();
40813 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40814 allowed.indexOf(String.fromCharCode(c)) === -1
40820 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40824 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40829 this.inputEl().on("keypress", keyPress, this);
40833 onTriggerClick : function(e)
40840 this.loadNext = false;
40842 if(this.isExpanded()){
40847 this.hasFocus = true;
40849 if(this.triggerAction == 'all') {
40850 this.doQuery(this.allQuery, true);
40854 this.doQuery(this.getRawValue());
40857 getCurrency : function()
40859 var v = this.currencyEl().getValue();
40864 restrictHeight : function()
40866 this.list.alignTo(this.currencyEl(), this.listAlign);
40867 this.list.alignTo(this.currencyEl(), this.listAlign);
40870 onViewClick : function(view, doFocus, el, e)
40872 var index = this.view.getSelectedIndexes()[0];
40874 var r = this.store.getAt(index);
40877 this.onSelect(r, index);
40881 onSelect : function(record, index){
40883 if(this.fireEvent('beforeselect', this, record, index) !== false){
40885 this.setFromCurrencyData(index > -1 ? record.data : false);
40889 this.fireEvent('select', this, record, index);
40893 setFromCurrencyData : function(o)
40897 this.lastCurrency = o;
40899 if (this.currencyField) {
40900 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40902 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40905 this.lastSelectionText = currency;
40907 //setting default currency
40908 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40909 this.setCurrency(this.defaultCurrency);
40913 this.setCurrency(currency);
40916 setFromData : function(o)
40920 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40922 this.setFromCurrencyData(c);
40927 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40929 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40932 this.setValue(value);
40936 setCurrency : function(v)
40938 this.currencyValue = v;
40941 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40946 setValue : function(v)
40948 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40954 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40956 this.inputEl().dom.value = (v == '') ? '' :
40957 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40959 if(!this.allowZero && v === '0') {
40960 this.hiddenEl().dom.value = '';
40961 this.inputEl().dom.value = '';
40968 getRawValue : function()
40970 var v = this.inputEl().getValue();
40975 getValue : function()
40977 return this.fixPrecision(this.parseValue(this.getRawValue()));
40980 parseValue : function(value)
40982 if(this.thousandsDelimiter) {
40984 r = new RegExp(",", "g");
40985 value = value.replace(r, "");
40988 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40989 return isNaN(value) ? '' : value;
40993 fixPrecision : function(value)
40995 if(this.thousandsDelimiter) {
40997 r = new RegExp(",", "g");
40998 value = value.replace(r, "");
41001 var nan = isNaN(value);
41003 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41004 return nan ? '' : value;
41006 return parseFloat(value).toFixed(this.decimalPrecision);
41009 decimalPrecisionFcn : function(v)
41011 return Math.floor(v);
41014 validateValue : function(value)
41016 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41020 var num = this.parseValue(value);
41023 this.markInvalid(String.format(this.nanText, value));
41027 if(num < this.minValue){
41028 this.markInvalid(String.format(this.minText, this.minValue));
41032 if(num > this.maxValue){
41033 this.markInvalid(String.format(this.maxText, this.maxValue));
41040 validate : function()
41042 if(this.disabled || this.allowBlank){
41047 var currency = this.getCurrency();
41049 if(this.validateValue(this.getRawValue()) && currency.length){
41054 this.markInvalid();
41058 getName: function()
41063 beforeBlur : function()
41069 var v = this.parseValue(this.getRawValue());
41076 onBlur : function()
41080 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41081 //this.el.removeClass(this.focusClass);
41084 this.hasFocus = false;
41086 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41090 var v = this.getValue();
41092 if(String(v) !== String(this.startValue)){
41093 this.fireEvent('change', this, v, this.startValue);
41096 this.fireEvent("blur", this);
41099 inputEl : function()
41101 return this.el.select('.roo-money-amount-input', true).first();
41104 currencyEl : function()
41106 return this.el.select('.roo-money-currency-input', true).first();
41109 hiddenEl : function()
41111 return this.el.select('input.hidden-number-input',true).first();