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++) {
7120 var size_cls = w[j].split("-");
7122 if(!Number.isInteger(size_cls[1] * 1)) {
7126 if(!this.colModel.config[col_index][size_cls[0]]) {
7130 h_row[0].classList.replace(
7131 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7132 "col-"+size_cls[0]+"-"+size_cls[1]
7135 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7138 for(var i = 0; i < rows.length; i++) {
7140 for(var j = 0; w.length; j++) {
7142 var size_cls = w[j].split("-");
7144 if(!Number.isInteger(size_cls[1] * 1)) {
7148 if(!this.colModel.config[col_index][size_cls[0]]) {
7152 rows[i].classList.replace(
7153 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7154 "col-"+size_cls[0]+"-"+size_cls[1]
7171 * @class Roo.bootstrap.TableCell
7172 * @extends Roo.bootstrap.Component
7173 * Bootstrap TableCell class
7174 * @cfg {String} html cell contain text
7175 * @cfg {String} cls cell class
7176 * @cfg {String} tag cell tag (td|th) default td
7177 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7178 * @cfg {String} align Aligns the content in a cell
7179 * @cfg {String} axis Categorizes cells
7180 * @cfg {String} bgcolor Specifies the background color of a cell
7181 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7182 * @cfg {Number} colspan Specifies the number of columns a cell should span
7183 * @cfg {String} headers Specifies one or more header cells a cell is related to
7184 * @cfg {Number} height Sets the height of a cell
7185 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7186 * @cfg {Number} rowspan Sets the number of rows a cell should span
7187 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7188 * @cfg {String} valign Vertical aligns the content in a cell
7189 * @cfg {Number} width Specifies the width of a cell
7192 * Create a new TableCell
7193 * @param {Object} config The config object
7196 Roo.bootstrap.TableCell = function(config){
7197 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7200 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7220 getAutoCreate : function(){
7221 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7241 cfg.align=this.align
7247 cfg.bgcolor=this.bgcolor
7250 cfg.charoff=this.charoff
7253 cfg.colspan=this.colspan
7256 cfg.headers=this.headers
7259 cfg.height=this.height
7262 cfg.nowrap=this.nowrap
7265 cfg.rowspan=this.rowspan
7268 cfg.scope=this.scope
7271 cfg.valign=this.valign
7274 cfg.width=this.width
7293 * @class Roo.bootstrap.TableRow
7294 * @extends Roo.bootstrap.Component
7295 * Bootstrap TableRow class
7296 * @cfg {String} cls row class
7297 * @cfg {String} align Aligns the content in a table row
7298 * @cfg {String} bgcolor Specifies a background color for a table row
7299 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7300 * @cfg {String} valign Vertical aligns the content in a table row
7303 * Create a new TableRow
7304 * @param {Object} config The config object
7307 Roo.bootstrap.TableRow = function(config){
7308 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7311 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7319 getAutoCreate : function(){
7320 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7330 cfg.align = this.align;
7333 cfg.bgcolor = this.bgcolor;
7336 cfg.charoff = this.charoff;
7339 cfg.valign = this.valign;
7357 * @class Roo.bootstrap.TableBody
7358 * @extends Roo.bootstrap.Component
7359 * Bootstrap TableBody class
7360 * @cfg {String} cls element class
7361 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7362 * @cfg {String} align Aligns the content inside the element
7363 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7364 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7367 * Create a new TableBody
7368 * @param {Object} config The config object
7371 Roo.bootstrap.TableBody = function(config){
7372 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7375 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7383 getAutoCreate : function(){
7384 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7398 cfg.align = this.align;
7401 cfg.charoff = this.charoff;
7404 cfg.valign = this.valign;
7411 // initEvents : function()
7418 // this.store = Roo.factory(this.store, Roo.data);
7419 // this.store.on('load', this.onLoad, this);
7421 // this.store.load();
7425 // onLoad: function ()
7427 // this.fireEvent('load', this);
7437 * Ext JS Library 1.1.1
7438 * Copyright(c) 2006-2007, Ext JS, LLC.
7440 * Originally Released Under LGPL - original licence link has changed is not relivant.
7443 * <script type="text/javascript">
7446 // as we use this in bootstrap.
7447 Roo.namespace('Roo.form');
7449 * @class Roo.form.Action
7450 * Internal Class used to handle form actions
7452 * @param {Roo.form.BasicForm} el The form element or its id
7453 * @param {Object} config Configuration options
7458 // define the action interface
7459 Roo.form.Action = function(form, options){
7461 this.options = options || {};
7464 * Client Validation Failed
7467 Roo.form.Action.CLIENT_INVALID = 'client';
7469 * Server Validation Failed
7472 Roo.form.Action.SERVER_INVALID = 'server';
7474 * Connect to Server Failed
7477 Roo.form.Action.CONNECT_FAILURE = 'connect';
7479 * Reading Data from Server Failed
7482 Roo.form.Action.LOAD_FAILURE = 'load';
7484 Roo.form.Action.prototype = {
7486 failureType : undefined,
7487 response : undefined,
7491 run : function(options){
7496 success : function(response){
7501 handleResponse : function(response){
7505 // default connection failure
7506 failure : function(response){
7508 this.response = response;
7509 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7510 this.form.afterAction(this, false);
7513 processResponse : function(response){
7514 this.response = response;
7515 if(!response.responseText){
7518 this.result = this.handleResponse(response);
7522 // utility functions used internally
7523 getUrl : function(appendParams){
7524 var url = this.options.url || this.form.url || this.form.el.dom.action;
7526 var p = this.getParams();
7528 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7534 getMethod : function(){
7535 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7538 getParams : function(){
7539 var bp = this.form.baseParams;
7540 var p = this.options.params;
7542 if(typeof p == "object"){
7543 p = Roo.urlEncode(Roo.applyIf(p, bp));
7544 }else if(typeof p == 'string' && bp){
7545 p += '&' + Roo.urlEncode(bp);
7548 p = Roo.urlEncode(bp);
7553 createCallback : function(){
7555 success: this.success,
7556 failure: this.failure,
7558 timeout: (this.form.timeout*1000),
7559 upload: this.form.fileUpload ? this.success : undefined
7564 Roo.form.Action.Submit = function(form, options){
7565 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7568 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7571 haveProgress : false,
7572 uploadComplete : false,
7574 // uploadProgress indicator.
7575 uploadProgress : function()
7577 if (!this.form.progressUrl) {
7581 if (!this.haveProgress) {
7582 Roo.MessageBox.progress("Uploading", "Uploading");
7584 if (this.uploadComplete) {
7585 Roo.MessageBox.hide();
7589 this.haveProgress = true;
7591 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7593 var c = new Roo.data.Connection();
7595 url : this.form.progressUrl,
7600 success : function(req){
7601 //console.log(data);
7605 rdata = Roo.decode(req.responseText)
7607 Roo.log("Invalid data from server..");
7611 if (!rdata || !rdata.success) {
7613 Roo.MessageBox.alert(Roo.encode(rdata));
7616 var data = rdata.data;
7618 if (this.uploadComplete) {
7619 Roo.MessageBox.hide();
7624 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7625 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7628 this.uploadProgress.defer(2000,this);
7631 failure: function(data) {
7632 Roo.log('progress url failed ');
7643 // run get Values on the form, so it syncs any secondary forms.
7644 this.form.getValues();
7646 var o = this.options;
7647 var method = this.getMethod();
7648 var isPost = method == 'POST';
7649 if(o.clientValidation === false || this.form.isValid()){
7651 if (this.form.progressUrl) {
7652 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7653 (new Date() * 1) + '' + Math.random());
7658 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7659 form:this.form.el.dom,
7660 url:this.getUrl(!isPost),
7662 params:isPost ? this.getParams() : null,
7663 isUpload: this.form.fileUpload
7666 this.uploadProgress();
7668 }else if (o.clientValidation !== false){ // client validation failed
7669 this.failureType = Roo.form.Action.CLIENT_INVALID;
7670 this.form.afterAction(this, false);
7674 success : function(response)
7676 this.uploadComplete= true;
7677 if (this.haveProgress) {
7678 Roo.MessageBox.hide();
7682 var result = this.processResponse(response);
7683 if(result === true || result.success){
7684 this.form.afterAction(this, true);
7688 this.form.markInvalid(result.errors);
7689 this.failureType = Roo.form.Action.SERVER_INVALID;
7691 this.form.afterAction(this, false);
7693 failure : function(response)
7695 this.uploadComplete= true;
7696 if (this.haveProgress) {
7697 Roo.MessageBox.hide();
7700 this.response = response;
7701 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7702 this.form.afterAction(this, false);
7705 handleResponse : function(response){
7706 if(this.form.errorReader){
7707 var rs = this.form.errorReader.read(response);
7710 for(var i = 0, len = rs.records.length; i < len; i++) {
7711 var r = rs.records[i];
7715 if(errors.length < 1){
7719 success : rs.success,
7725 ret = Roo.decode(response.responseText);
7729 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7739 Roo.form.Action.Load = function(form, options){
7740 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7741 this.reader = this.form.reader;
7744 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7749 Roo.Ajax.request(Roo.apply(
7750 this.createCallback(), {
7751 method:this.getMethod(),
7752 url:this.getUrl(false),
7753 params:this.getParams()
7757 success : function(response){
7759 var result = this.processResponse(response);
7760 if(result === true || !result.success || !result.data){
7761 this.failureType = Roo.form.Action.LOAD_FAILURE;
7762 this.form.afterAction(this, false);
7765 this.form.clearInvalid();
7766 this.form.setValues(result.data);
7767 this.form.afterAction(this, true);
7770 handleResponse : function(response){
7771 if(this.form.reader){
7772 var rs = this.form.reader.read(response);
7773 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7775 success : rs.success,
7779 return Roo.decode(response.responseText);
7783 Roo.form.Action.ACTION_TYPES = {
7784 'load' : Roo.form.Action.Load,
7785 'submit' : Roo.form.Action.Submit
7794 * @class Roo.bootstrap.Form
7795 * @extends Roo.bootstrap.Component
7796 * Bootstrap Form class
7797 * @cfg {String} method GET | POST (default POST)
7798 * @cfg {String} labelAlign top | left (default top)
7799 * @cfg {String} align left | right - for navbars
7800 * @cfg {Boolean} loadMask load mask when submit (default true)
7805 * @param {Object} config The config object
7809 Roo.bootstrap.Form = function(config){
7811 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7813 Roo.bootstrap.Form.popover.apply();
7817 * @event clientvalidation
7818 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7819 * @param {Form} this
7820 * @param {Boolean} valid true if the form has passed client-side validation
7822 clientvalidation: true,
7824 * @event beforeaction
7825 * Fires before any action is performed. Return false to cancel the action.
7826 * @param {Form} this
7827 * @param {Action} action The action to be performed
7831 * @event actionfailed
7832 * Fires when an action fails.
7833 * @param {Form} this
7834 * @param {Action} action The action that failed
7836 actionfailed : true,
7838 * @event actioncomplete
7839 * Fires when an action is completed.
7840 * @param {Form} this
7841 * @param {Action} action The action that completed
7843 actioncomplete : true
7847 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7850 * @cfg {String} method
7851 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7856 * The URL to use for form actions if one isn't supplied in the action options.
7859 * @cfg {Boolean} fileUpload
7860 * Set to true if this form is a file upload.
7864 * @cfg {Object} baseParams
7865 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7869 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7873 * @cfg {Sting} align (left|right) for navbar forms
7878 activeAction : null,
7881 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7882 * element by passing it or its id or mask the form itself by passing in true.
7885 waitMsgTarget : false,
7890 * @cfg {Boolean} errorMask (true|false) default false
7895 * @cfg {Number} maskOffset Default 100
7900 * @cfg {Boolean} maskBody
7904 getAutoCreate : function(){
7908 method : this.method || 'POST',
7909 id : this.id || Roo.id(),
7912 if (this.parent().xtype.match(/^Nav/)) {
7913 cfg.cls = 'navbar-form navbar-' + this.align;
7917 if (this.labelAlign == 'left' ) {
7918 cfg.cls += ' form-horizontal';
7924 initEvents : function()
7926 this.el.on('submit', this.onSubmit, this);
7927 // this was added as random key presses on the form where triggering form submit.
7928 this.el.on('keypress', function(e) {
7929 if (e.getCharCode() != 13) {
7932 // we might need to allow it for textareas.. and some other items.
7933 // check e.getTarget().
7935 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7939 Roo.log("keypress blocked");
7947 onSubmit : function(e){
7952 * Returns true if client-side validation on the form is successful.
7955 isValid : function(){
7956 var items = this.getItems();
7960 items.each(function(f){
7966 Roo.log('invalid field: ' + f.name);
7970 if(!target && f.el.isVisible(true)){
7976 if(this.errorMask && !valid){
7977 Roo.bootstrap.Form.popover.mask(this, target);
7984 * Returns true if any fields in this form have changed since their original load.
7987 isDirty : function(){
7989 var items = this.getItems();
7990 items.each(function(f){
8000 * Performs a predefined action (submit or load) or custom actions you define on this form.
8001 * @param {String} actionName The name of the action type
8002 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8003 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8004 * accept other config options):
8006 Property Type Description
8007 ---------------- --------------- ----------------------------------------------------------------------------------
8008 url String The url for the action (defaults to the form's url)
8009 method String The form method to use (defaults to the form's method, or POST if not defined)
8010 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8011 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8012 validate the form on the client (defaults to false)
8014 * @return {BasicForm} this
8016 doAction : function(action, options){
8017 if(typeof action == 'string'){
8018 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8020 if(this.fireEvent('beforeaction', this, action) !== false){
8021 this.beforeAction(action);
8022 action.run.defer(100, action);
8028 beforeAction : function(action){
8029 var o = action.options;
8034 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8036 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8039 // not really supported yet.. ??
8041 //if(this.waitMsgTarget === true){
8042 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8043 //}else if(this.waitMsgTarget){
8044 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8045 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8047 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8053 afterAction : function(action, success){
8054 this.activeAction = null;
8055 var o = action.options;
8060 Roo.get(document.body).unmask();
8066 //if(this.waitMsgTarget === true){
8067 // this.el.unmask();
8068 //}else if(this.waitMsgTarget){
8069 // this.waitMsgTarget.unmask();
8071 // Roo.MessageBox.updateProgress(1);
8072 // Roo.MessageBox.hide();
8079 Roo.callback(o.success, o.scope, [this, action]);
8080 this.fireEvent('actioncomplete', this, action);
8084 // failure condition..
8085 // we have a scenario where updates need confirming.
8086 // eg. if a locking scenario exists..
8087 // we look for { errors : { needs_confirm : true }} in the response.
8089 (typeof(action.result) != 'undefined') &&
8090 (typeof(action.result.errors) != 'undefined') &&
8091 (typeof(action.result.errors.needs_confirm) != 'undefined')
8094 Roo.log("not supported yet");
8097 Roo.MessageBox.confirm(
8098 "Change requires confirmation",
8099 action.result.errorMsg,
8104 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8114 Roo.callback(o.failure, o.scope, [this, action]);
8115 // show an error message if no failed handler is set..
8116 if (!this.hasListener('actionfailed')) {
8117 Roo.log("need to add dialog support");
8119 Roo.MessageBox.alert("Error",
8120 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8121 action.result.errorMsg :
8122 "Saving Failed, please check your entries or try again"
8127 this.fireEvent('actionfailed', this, action);
8132 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8133 * @param {String} id The value to search for
8136 findField : function(id){
8137 var items = this.getItems();
8138 var field = items.get(id);
8140 items.each(function(f){
8141 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8148 return field || null;
8151 * Mark fields in this form invalid in bulk.
8152 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8153 * @return {BasicForm} this
8155 markInvalid : function(errors){
8156 if(errors instanceof Array){
8157 for(var i = 0, len = errors.length; i < len; i++){
8158 var fieldError = errors[i];
8159 var f = this.findField(fieldError.id);
8161 f.markInvalid(fieldError.msg);
8167 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8168 field.markInvalid(errors[id]);
8172 //Roo.each(this.childForms || [], function (f) {
8173 // f.markInvalid(errors);
8180 * Set values for fields in this form in bulk.
8181 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8182 * @return {BasicForm} this
8184 setValues : function(values){
8185 if(values instanceof Array){ // array of objects
8186 for(var i = 0, len = values.length; i < len; i++){
8188 var f = this.findField(v.id);
8190 f.setValue(v.value);
8191 if(this.trackResetOnLoad){
8192 f.originalValue = f.getValue();
8196 }else{ // object hash
8199 if(typeof values[id] != 'function' && (field = this.findField(id))){
8201 if (field.setFromData &&
8203 field.displayField &&
8204 // combos' with local stores can
8205 // be queried via setValue()
8206 // to set their value..
8207 (field.store && !field.store.isLocal)
8211 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8212 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8213 field.setFromData(sd);
8215 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8217 field.setFromData(values);
8220 field.setValue(values[id]);
8224 if(this.trackResetOnLoad){
8225 field.originalValue = field.getValue();
8231 //Roo.each(this.childForms || [], function (f) {
8232 // f.setValues(values);
8239 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8240 * they are returned as an array.
8241 * @param {Boolean} asString
8244 getValues : function(asString){
8245 //if (this.childForms) {
8246 // copy values from the child forms
8247 // Roo.each(this.childForms, function (f) {
8248 // this.setValues(f.getValues());
8254 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8255 if(asString === true){
8258 return Roo.urlDecode(fs);
8262 * Returns the fields in this form as an object with key/value pairs.
8263 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8266 getFieldValues : function(with_hidden)
8268 var items = this.getItems();
8270 items.each(function(f){
8276 var v = f.getValue();
8278 if (f.inputType =='radio') {
8279 if (typeof(ret[f.getName()]) == 'undefined') {
8280 ret[f.getName()] = ''; // empty..
8283 if (!f.el.dom.checked) {
8291 if(f.xtype == 'MoneyField'){
8292 ret[f.currencyName] = f.getCurrency();
8295 // not sure if this supported any more..
8296 if ((typeof(v) == 'object') && f.getRawValue) {
8297 v = f.getRawValue() ; // dates..
8299 // combo boxes where name != hiddenName...
8300 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8301 ret[f.name] = f.getRawValue();
8303 ret[f.getName()] = v;
8310 * Clears all invalid messages in this form.
8311 * @return {BasicForm} this
8313 clearInvalid : function(){
8314 var items = this.getItems();
8316 items.each(function(f){
8325 * @return {BasicForm} this
8328 var items = this.getItems();
8329 items.each(function(f){
8333 Roo.each(this.childForms || [], function (f) {
8341 getItems : function()
8343 var r=new Roo.util.MixedCollection(false, function(o){
8344 return o.id || (o.id = Roo.id());
8346 var iter = function(el) {
8353 Roo.each(el.items,function(e) {
8362 hideFields : function(items)
8364 Roo.each(items, function(i){
8366 var f = this.findField(i);
8377 showFields : function(items)
8379 Roo.each(items, function(i){
8381 var f = this.findField(i);
8394 Roo.apply(Roo.bootstrap.Form, {
8421 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8422 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8423 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8424 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8427 this.maskEl.top.enableDisplayMode("block");
8428 this.maskEl.left.enableDisplayMode("block");
8429 this.maskEl.bottom.enableDisplayMode("block");
8430 this.maskEl.right.enableDisplayMode("block");
8432 this.toolTip = new Roo.bootstrap.Tooltip({
8433 cls : 'roo-form-error-popover',
8435 'left' : ['r-l', [-2,0], 'right'],
8436 'right' : ['l-r', [2,0], 'left'],
8437 'bottom' : ['tl-bl', [0,2], 'top'],
8438 'top' : [ 'bl-tl', [0,-2], 'bottom']
8442 this.toolTip.render(Roo.get(document.body));
8444 this.toolTip.el.enableDisplayMode("block");
8446 Roo.get(document.body).on('click', function(){
8450 Roo.get(document.body).on('touchstart', function(){
8454 this.isApplied = true
8457 mask : function(form, target)
8461 this.target = target;
8463 if(!this.form.errorMask || !target.el){
8467 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8469 Roo.log(scrollable);
8471 var ot = this.target.el.calcOffsetsTo(scrollable);
8473 var scrollTo = ot[1] - this.form.maskOffset;
8475 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8477 scrollable.scrollTo('top', scrollTo);
8479 var box = this.target.el.getBox();
8481 var zIndex = Roo.bootstrap.Modal.zIndex++;
8484 this.maskEl.top.setStyle('position', 'absolute');
8485 this.maskEl.top.setStyle('z-index', zIndex);
8486 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8487 this.maskEl.top.setLeft(0);
8488 this.maskEl.top.setTop(0);
8489 this.maskEl.top.show();
8491 this.maskEl.left.setStyle('position', 'absolute');
8492 this.maskEl.left.setStyle('z-index', zIndex);
8493 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8494 this.maskEl.left.setLeft(0);
8495 this.maskEl.left.setTop(box.y - this.padding);
8496 this.maskEl.left.show();
8498 this.maskEl.bottom.setStyle('position', 'absolute');
8499 this.maskEl.bottom.setStyle('z-index', zIndex);
8500 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8501 this.maskEl.bottom.setLeft(0);
8502 this.maskEl.bottom.setTop(box.bottom + this.padding);
8503 this.maskEl.bottom.show();
8505 this.maskEl.right.setStyle('position', 'absolute');
8506 this.maskEl.right.setStyle('z-index', zIndex);
8507 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8508 this.maskEl.right.setLeft(box.right + this.padding);
8509 this.maskEl.right.setTop(box.y - this.padding);
8510 this.maskEl.right.show();
8512 this.toolTip.bindEl = this.target.el;
8514 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8516 var tip = this.target.blankText;
8518 if(this.target.getValue() !== '' ) {
8520 if (this.target.invalidText.length) {
8521 tip = this.target.invalidText;
8522 } else if (this.target.regexText.length){
8523 tip = this.target.regexText;
8527 this.toolTip.show(tip);
8529 this.intervalID = window.setInterval(function() {
8530 Roo.bootstrap.Form.popover.unmask();
8533 window.onwheel = function(){ return false;};
8535 (function(){ this.isMasked = true; }).defer(500, this);
8541 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8545 this.maskEl.top.setStyle('position', 'absolute');
8546 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8547 this.maskEl.top.hide();
8549 this.maskEl.left.setStyle('position', 'absolute');
8550 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8551 this.maskEl.left.hide();
8553 this.maskEl.bottom.setStyle('position', 'absolute');
8554 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8555 this.maskEl.bottom.hide();
8557 this.maskEl.right.setStyle('position', 'absolute');
8558 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8559 this.maskEl.right.hide();
8561 this.toolTip.hide();
8563 this.toolTip.el.hide();
8565 window.onwheel = function(){ return true;};
8567 if(this.intervalID){
8568 window.clearInterval(this.intervalID);
8569 this.intervalID = false;
8572 this.isMasked = false;
8582 * Ext JS Library 1.1.1
8583 * Copyright(c) 2006-2007, Ext JS, LLC.
8585 * Originally Released Under LGPL - original licence link has changed is not relivant.
8588 * <script type="text/javascript">
8591 * @class Roo.form.VTypes
8592 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8595 Roo.form.VTypes = function(){
8596 // closure these in so they are only created once.
8597 var alpha = /^[a-zA-Z_]+$/;
8598 var alphanum = /^[a-zA-Z0-9_]+$/;
8599 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8600 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8602 // All these messages and functions are configurable
8605 * The function used to validate email addresses
8606 * @param {String} value The email address
8608 'email' : function(v){
8609 return email.test(v);
8612 * The error text to display when the email validation function returns false
8615 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8617 * The keystroke filter mask to be applied on email input
8620 'emailMask' : /[a-z0-9_\.\-@]/i,
8623 * The function used to validate URLs
8624 * @param {String} value The URL
8626 'url' : function(v){
8630 * The error text to display when the url validation function returns false
8633 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8636 * The function used to validate alpha values
8637 * @param {String} value The value
8639 'alpha' : function(v){
8640 return alpha.test(v);
8643 * The error text to display when the alpha validation function returns false
8646 'alphaText' : 'This field should only contain letters and _',
8648 * The keystroke filter mask to be applied on alpha input
8651 'alphaMask' : /[a-z_]/i,
8654 * The function used to validate alphanumeric values
8655 * @param {String} value The value
8657 'alphanum' : function(v){
8658 return alphanum.test(v);
8661 * The error text to display when the alphanumeric validation function returns false
8664 'alphanumText' : 'This field should only contain letters, numbers and _',
8666 * The keystroke filter mask to be applied on alphanumeric input
8669 'alphanumMask' : /[a-z0-9_]/i
8679 * @class Roo.bootstrap.Input
8680 * @extends Roo.bootstrap.Component
8681 * Bootstrap Input class
8682 * @cfg {Boolean} disabled is it disabled
8683 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8684 * @cfg {String} name name of the input
8685 * @cfg {string} fieldLabel - the label associated
8686 * @cfg {string} placeholder - placeholder to put in text.
8687 * @cfg {string} before - input group add on before
8688 * @cfg {string} after - input group add on after
8689 * @cfg {string} size - (lg|sm) or leave empty..
8690 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8691 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8692 * @cfg {Number} md colspan out of 12 for computer-sized screens
8693 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8694 * @cfg {string} value default value of the input
8695 * @cfg {Number} labelWidth set the width of label
8696 * @cfg {Number} labellg set the width of label (1-12)
8697 * @cfg {Number} labelmd set the width of label (1-12)
8698 * @cfg {Number} labelsm set the width of label (1-12)
8699 * @cfg {Number} labelxs set the width of label (1-12)
8700 * @cfg {String} labelAlign (top|left)
8701 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8702 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8703 * @cfg {String} indicatorpos (left|right) default left
8704 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8705 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8707 * @cfg {String} align (left|center|right) Default left
8708 * @cfg {Boolean} forceFeedback (true|false) Default false
8711 * Create a new Input
8712 * @param {Object} config The config object
8715 Roo.bootstrap.Input = function(config){
8717 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8722 * Fires when this field receives input focus.
8723 * @param {Roo.form.Field} this
8728 * Fires when this field loses input focus.
8729 * @param {Roo.form.Field} this
8734 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8735 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8736 * @param {Roo.form.Field} this
8737 * @param {Roo.EventObject} e The event object
8742 * Fires just before the field blurs if the field value has changed.
8743 * @param {Roo.form.Field} this
8744 * @param {Mixed} newValue The new value
8745 * @param {Mixed} oldValue The original value
8750 * Fires after the field has been marked as invalid.
8751 * @param {Roo.form.Field} this
8752 * @param {String} msg The validation message
8757 * Fires after the field has been validated with no errors.
8758 * @param {Roo.form.Field} this
8763 * Fires after the key up
8764 * @param {Roo.form.Field} this
8765 * @param {Roo.EventObject} e The event Object
8771 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8773 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8774 automatic validation (defaults to "keyup").
8776 validationEvent : "keyup",
8778 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8780 validateOnBlur : true,
8782 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8784 validationDelay : 250,
8786 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8788 focusClass : "x-form-focus", // not needed???
8792 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8794 invalidClass : "has-warning",
8797 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8799 validClass : "has-success",
8802 * @cfg {Boolean} hasFeedback (true|false) default true
8807 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8809 invalidFeedbackClass : "glyphicon-warning-sign",
8812 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8814 validFeedbackClass : "glyphicon-ok",
8817 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8819 selectOnFocus : false,
8822 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8826 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8831 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8833 disableKeyFilter : false,
8836 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8840 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8844 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8846 blankText : "Please complete this mandatory field",
8849 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8853 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8855 maxLength : Number.MAX_VALUE,
8857 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8859 minLengthText : "The minimum length for this field is {0}",
8861 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8863 maxLengthText : "The maximum length for this field is {0}",
8867 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8868 * If available, this function will be called only after the basic validators all return true, and will be passed the
8869 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8873 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8874 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8875 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8879 * @cfg {String} regexText -- Depricated - use Invalid Text
8884 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8890 autocomplete: false,
8909 formatedValue : false,
8910 forceFeedback : false,
8912 indicatorpos : 'left',
8922 parentLabelAlign : function()
8925 while (parent.parent()) {
8926 parent = parent.parent();
8927 if (typeof(parent.labelAlign) !='undefined') {
8928 return parent.labelAlign;
8935 getAutoCreate : function()
8937 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8943 if(this.inputType != 'hidden'){
8944 cfg.cls = 'form-group' //input-group
8950 type : this.inputType,
8952 cls : 'form-control',
8953 placeholder : this.placeholder || '',
8954 autocomplete : this.autocomplete || 'new-password'
8957 if(this.capture.length){
8958 input.capture = this.capture;
8961 if(this.accept.length){
8962 input.accept = this.accept + "/*";
8966 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8969 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8970 input.maxLength = this.maxLength;
8973 if (this.disabled) {
8974 input.disabled=true;
8977 if (this.readOnly) {
8978 input.readonly=true;
8982 input.name = this.name;
8986 input.cls += ' input-' + this.size;
8990 ['xs','sm','md','lg'].map(function(size){
8991 if (settings[size]) {
8992 cfg.cls += ' col-' + size + '-' + settings[size];
8996 var inputblock = input;
9000 cls: 'glyphicon form-control-feedback'
9003 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9006 cls : 'has-feedback',
9014 if (this.before || this.after) {
9017 cls : 'input-group',
9021 if (this.before && typeof(this.before) == 'string') {
9023 inputblock.cn.push({
9025 cls : 'roo-input-before input-group-addon',
9029 if (this.before && typeof(this.before) == 'object') {
9030 this.before = Roo.factory(this.before);
9032 inputblock.cn.push({
9034 cls : 'roo-input-before input-group-' +
9035 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9039 inputblock.cn.push(input);
9041 if (this.after && typeof(this.after) == 'string') {
9042 inputblock.cn.push({
9044 cls : 'roo-input-after input-group-addon',
9048 if (this.after && typeof(this.after) == 'object') {
9049 this.after = Roo.factory(this.after);
9051 inputblock.cn.push({
9053 cls : 'roo-input-after input-group-' +
9054 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9058 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9059 inputblock.cls += ' has-feedback';
9060 inputblock.cn.push(feedback);
9064 if (align ==='left' && this.fieldLabel.length) {
9066 cfg.cls += ' roo-form-group-label-left';
9071 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9072 tooltip : 'This field is required'
9077 cls : 'control-label',
9078 html : this.fieldLabel
9089 var labelCfg = cfg.cn[1];
9090 var contentCfg = cfg.cn[2];
9092 if(this.indicatorpos == 'right'){
9097 cls : 'control-label',
9101 html : this.fieldLabel
9105 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9106 tooltip : 'This field is required'
9119 labelCfg = cfg.cn[0];
9120 contentCfg = cfg.cn[1];
9124 if(this.labelWidth > 12){
9125 labelCfg.style = "width: " + this.labelWidth + 'px';
9128 if(this.labelWidth < 13 && this.labelmd == 0){
9129 this.labelmd = this.labelWidth;
9132 if(this.labellg > 0){
9133 labelCfg.cls += ' col-lg-' + this.labellg;
9134 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9137 if(this.labelmd > 0){
9138 labelCfg.cls += ' col-md-' + this.labelmd;
9139 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9142 if(this.labelsm > 0){
9143 labelCfg.cls += ' col-sm-' + this.labelsm;
9144 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9147 if(this.labelxs > 0){
9148 labelCfg.cls += ' col-xs-' + this.labelxs;
9149 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9153 } else if ( this.fieldLabel.length) {
9158 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9159 tooltip : 'This field is required'
9163 //cls : 'input-group-addon',
9164 html : this.fieldLabel
9172 if(this.indicatorpos == 'right'){
9177 //cls : 'input-group-addon',
9178 html : this.fieldLabel
9183 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9184 tooltip : 'This field is required'
9204 if (this.parentType === 'Navbar' && this.parent().bar) {
9205 cfg.cls += ' navbar-form';
9208 if (this.parentType === 'NavGroup') {
9209 cfg.cls += ' navbar-form';
9217 * return the real input element.
9219 inputEl: function ()
9221 return this.el.select('input.form-control',true).first();
9224 tooltipEl : function()
9226 return this.inputEl();
9229 indicatorEl : function()
9231 var indicator = this.el.select('i.roo-required-indicator',true).first();
9241 setDisabled : function(v)
9243 var i = this.inputEl().dom;
9245 i.removeAttribute('disabled');
9249 i.setAttribute('disabled','true');
9251 initEvents : function()
9254 this.inputEl().on("keydown" , this.fireKey, this);
9255 this.inputEl().on("focus", this.onFocus, this);
9256 this.inputEl().on("blur", this.onBlur, this);
9258 this.inputEl().relayEvent('keyup', this);
9260 this.indicator = this.indicatorEl();
9263 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9266 // reference to original value for reset
9267 this.originalValue = this.getValue();
9268 //Roo.form.TextField.superclass.initEvents.call(this);
9269 if(this.validationEvent == 'keyup'){
9270 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9271 this.inputEl().on('keyup', this.filterValidation, this);
9273 else if(this.validationEvent !== false){
9274 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9277 if(this.selectOnFocus){
9278 this.on("focus", this.preFocus, this);
9281 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9282 this.inputEl().on("keypress", this.filterKeys, this);
9284 this.inputEl().relayEvent('keypress', this);
9287 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9288 this.el.on("click", this.autoSize, this);
9291 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9292 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9295 if (typeof(this.before) == 'object') {
9296 this.before.render(this.el.select('.roo-input-before',true).first());
9298 if (typeof(this.after) == 'object') {
9299 this.after.render(this.el.select('.roo-input-after',true).first());
9302 this.inputEl().on('change', this.onChange, this);
9305 filterValidation : function(e){
9306 if(!e.isNavKeyPress()){
9307 this.validationTask.delay(this.validationDelay);
9311 * Validates the field value
9312 * @return {Boolean} True if the value is valid, else false
9314 validate : function(){
9315 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9316 if(this.disabled || this.validateValue(this.getRawValue())){
9327 * Validates a value according to the field's validation rules and marks the field as invalid
9328 * if the validation fails
9329 * @param {Mixed} value The value to validate
9330 * @return {Boolean} True if the value is valid, else false
9332 validateValue : function(value)
9334 if(this.getVisibilityEl().hasClass('hidden')){
9338 if(value.length < 1) { // if it's blank
9339 if(this.allowBlank){
9345 if(value.length < this.minLength){
9348 if(value.length > this.maxLength){
9352 var vt = Roo.form.VTypes;
9353 if(!vt[this.vtype](value, this)){
9357 if(typeof this.validator == "function"){
9358 var msg = this.validator(value);
9362 if (typeof(msg) == 'string') {
9363 this.invalidText = msg;
9367 if(this.regex && !this.regex.test(value)){
9375 fireKey : function(e){
9376 //Roo.log('field ' + e.getKey());
9377 if(e.isNavKeyPress()){
9378 this.fireEvent("specialkey", this, e);
9381 focus : function (selectText){
9383 this.inputEl().focus();
9384 if(selectText === true){
9385 this.inputEl().dom.select();
9391 onFocus : function(){
9392 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9393 // this.el.addClass(this.focusClass);
9396 this.hasFocus = true;
9397 this.startValue = this.getValue();
9398 this.fireEvent("focus", this);
9402 beforeBlur : Roo.emptyFn,
9406 onBlur : function(){
9408 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9409 //this.el.removeClass(this.focusClass);
9411 this.hasFocus = false;
9412 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9415 var v = this.getValue();
9416 if(String(v) !== String(this.startValue)){
9417 this.fireEvent('change', this, v, this.startValue);
9419 this.fireEvent("blur", this);
9422 onChange : function(e)
9424 var v = this.getValue();
9425 if(String(v) !== String(this.startValue)){
9426 this.fireEvent('change', this, v, this.startValue);
9432 * Resets the current field value to the originally loaded value and clears any validation messages
9435 this.setValue(this.originalValue);
9439 * Returns the name of the field
9440 * @return {Mixed} name The name field
9442 getName: function(){
9446 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9447 * @return {Mixed} value The field value
9449 getValue : function(){
9451 var v = this.inputEl().getValue();
9456 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9457 * @return {Mixed} value The field value
9459 getRawValue : function(){
9460 var v = this.inputEl().getValue();
9466 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9467 * @param {Mixed} value The value to set
9469 setRawValue : function(v){
9470 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9473 selectText : function(start, end){
9474 var v = this.getRawValue();
9476 start = start === undefined ? 0 : start;
9477 end = end === undefined ? v.length : end;
9478 var d = this.inputEl().dom;
9479 if(d.setSelectionRange){
9480 d.setSelectionRange(start, end);
9481 }else if(d.createTextRange){
9482 var range = d.createTextRange();
9483 range.moveStart("character", start);
9484 range.moveEnd("character", v.length-end);
9491 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9492 * @param {Mixed} value The value to set
9494 setValue : function(v){
9497 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9503 processValue : function(value){
9504 if(this.stripCharsRe){
9505 var newValue = value.replace(this.stripCharsRe, '');
9506 if(newValue !== value){
9507 this.setRawValue(newValue);
9514 preFocus : function(){
9516 if(this.selectOnFocus){
9517 this.inputEl().dom.select();
9520 filterKeys : function(e){
9522 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9525 var c = e.getCharCode(), cc = String.fromCharCode(c);
9526 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9529 if(!this.maskRe.test(cc)){
9534 * Clear any invalid styles/messages for this field
9536 clearInvalid : function(){
9538 if(!this.el || this.preventMark){ // not rendered
9543 this.el.removeClass(this.invalidClass);
9545 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9547 var feedback = this.el.select('.form-control-feedback', true).first();
9550 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9556 this.indicator.removeClass('visible');
9557 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9560 this.fireEvent('valid', this);
9564 * Mark this field as valid
9566 markValid : function()
9568 if(!this.el || this.preventMark){ // not rendered...
9572 this.el.removeClass([this.invalidClass, this.validClass]);
9574 var feedback = this.el.select('.form-control-feedback', true).first();
9577 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9581 this.indicator.removeClass('visible');
9582 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9589 if(this.allowBlank && !this.getRawValue().length){
9593 this.el.addClass(this.validClass);
9595 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9597 var feedback = this.el.select('.form-control-feedback', true).first();
9600 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9601 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9606 this.fireEvent('valid', this);
9610 * Mark this field as invalid
9611 * @param {String} msg The validation message
9613 markInvalid : function(msg)
9615 if(!this.el || this.preventMark){ // not rendered
9619 this.el.removeClass([this.invalidClass, this.validClass]);
9621 var feedback = this.el.select('.form-control-feedback', true).first();
9624 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9631 if(this.allowBlank && !this.getRawValue().length){
9636 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9637 this.indicator.addClass('visible');
9640 this.el.addClass(this.invalidClass);
9642 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9644 var feedback = this.el.select('.form-control-feedback', true).first();
9647 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9649 if(this.getValue().length || this.forceFeedback){
9650 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9657 this.fireEvent('invalid', this, msg);
9660 SafariOnKeyDown : function(event)
9662 // this is a workaround for a password hang bug on chrome/ webkit.
9663 if (this.inputEl().dom.type != 'password') {
9667 var isSelectAll = false;
9669 if(this.inputEl().dom.selectionEnd > 0){
9670 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9672 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9673 event.preventDefault();
9678 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9680 event.preventDefault();
9681 // this is very hacky as keydown always get's upper case.
9683 var cc = String.fromCharCode(event.getCharCode());
9684 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9688 adjustWidth : function(tag, w){
9689 tag = tag.toLowerCase();
9690 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9691 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9695 if(tag == 'textarea'){
9698 }else if(Roo.isOpera){
9702 if(tag == 'textarea'){
9710 setFieldLabel : function(v)
9717 var ar = this.el.select('label > span',true);
9719 if (ar.elements.length) {
9720 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9721 this.fieldLabel = v;
9725 var br = this.el.select('label',true);
9727 if(br.elements.length) {
9728 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9729 this.fieldLabel = v;
9733 Roo.log('Cannot Found any of label > span || label in input');
9737 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9738 this.fieldLabel = v;
9753 * @class Roo.bootstrap.TextArea
9754 * @extends Roo.bootstrap.Input
9755 * Bootstrap TextArea class
9756 * @cfg {Number} cols Specifies the visible width of a text area
9757 * @cfg {Number} rows Specifies the visible number of lines in a text area
9758 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9759 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9760 * @cfg {string} html text
9763 * Create a new TextArea
9764 * @param {Object} config The config object
9767 Roo.bootstrap.TextArea = function(config){
9768 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9772 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9782 getAutoCreate : function(){
9784 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9790 if(this.inputType != 'hidden'){
9791 cfg.cls = 'form-group' //input-group
9799 value : this.value || '',
9800 html: this.html || '',
9801 cls : 'form-control',
9802 placeholder : this.placeholder || ''
9806 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9807 input.maxLength = this.maxLength;
9811 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9815 input.cols = this.cols;
9818 if (this.readOnly) {
9819 input.readonly = true;
9823 input.name = this.name;
9827 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9831 ['xs','sm','md','lg'].map(function(size){
9832 if (settings[size]) {
9833 cfg.cls += ' col-' + size + '-' + settings[size];
9837 var inputblock = input;
9839 if(this.hasFeedback && !this.allowBlank){
9843 cls: 'glyphicon form-control-feedback'
9847 cls : 'has-feedback',
9856 if (this.before || this.after) {
9859 cls : 'input-group',
9863 inputblock.cn.push({
9865 cls : 'input-group-addon',
9870 inputblock.cn.push(input);
9872 if(this.hasFeedback && !this.allowBlank){
9873 inputblock.cls += ' has-feedback';
9874 inputblock.cn.push(feedback);
9878 inputblock.cn.push({
9880 cls : 'input-group-addon',
9887 if (align ==='left' && this.fieldLabel.length) {
9892 cls : 'control-label',
9893 html : this.fieldLabel
9904 if(this.labelWidth > 12){
9905 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9908 if(this.labelWidth < 13 && this.labelmd == 0){
9909 this.labelmd = this.labelWidth;
9912 if(this.labellg > 0){
9913 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9914 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9917 if(this.labelmd > 0){
9918 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9919 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9922 if(this.labelsm > 0){
9923 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9924 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9927 if(this.labelxs > 0){
9928 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9929 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9932 } else if ( this.fieldLabel.length) {
9937 //cls : 'input-group-addon',
9938 html : this.fieldLabel
9956 if (this.disabled) {
9957 input.disabled=true;
9964 * return the real textarea element.
9966 inputEl: function ()
9968 return this.el.select('textarea.form-control',true).first();
9972 * Clear any invalid styles/messages for this field
9974 clearInvalid : function()
9977 if(!this.el || this.preventMark){ // not rendered
9981 var label = this.el.select('label', true).first();
9982 var icon = this.el.select('i.fa-star', true).first();
9988 this.el.removeClass(this.invalidClass);
9990 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9992 var feedback = this.el.select('.form-control-feedback', true).first();
9995 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10000 this.fireEvent('valid', this);
10004 * Mark this field as valid
10006 markValid : function()
10008 if(!this.el || this.preventMark){ // not rendered
10012 this.el.removeClass([this.invalidClass, this.validClass]);
10014 var feedback = this.el.select('.form-control-feedback', true).first();
10017 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10020 if(this.disabled || this.allowBlank){
10024 var label = this.el.select('label', true).first();
10025 var icon = this.el.select('i.fa-star', true).first();
10031 this.el.addClass(this.validClass);
10033 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10035 var feedback = this.el.select('.form-control-feedback', true).first();
10038 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10039 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10044 this.fireEvent('valid', this);
10048 * Mark this field as invalid
10049 * @param {String} msg The validation message
10051 markInvalid : function(msg)
10053 if(!this.el || this.preventMark){ // not rendered
10057 this.el.removeClass([this.invalidClass, this.validClass]);
10059 var feedback = this.el.select('.form-control-feedback', true).first();
10062 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10065 if(this.disabled || this.allowBlank){
10069 var label = this.el.select('label', true).first();
10070 var icon = this.el.select('i.fa-star', true).first();
10072 if(!this.getValue().length && label && !icon){
10073 this.el.createChild({
10075 cls : 'text-danger fa fa-lg fa-star',
10076 tooltip : 'This field is required',
10077 style : 'margin-right:5px;'
10081 this.el.addClass(this.invalidClass);
10083 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10085 var feedback = this.el.select('.form-control-feedback', true).first();
10088 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10090 if(this.getValue().length || this.forceFeedback){
10091 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10098 this.fireEvent('invalid', this, msg);
10106 * trigger field - base class for combo..
10111 * @class Roo.bootstrap.TriggerField
10112 * @extends Roo.bootstrap.Input
10113 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10114 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10115 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10116 * for which you can provide a custom implementation. For example:
10118 var trigger = new Roo.bootstrap.TriggerField();
10119 trigger.onTriggerClick = myTriggerFn;
10120 trigger.applyTo('my-field');
10123 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10124 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10125 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10126 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10127 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10130 * Create a new TriggerField.
10131 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10132 * to the base TextField)
10134 Roo.bootstrap.TriggerField = function(config){
10135 this.mimicing = false;
10136 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10139 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10141 * @cfg {String} triggerClass A CSS class to apply to the trigger
10144 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10149 * @cfg {Boolean} removable (true|false) special filter default false
10153 /** @cfg {Boolean} grow @hide */
10154 /** @cfg {Number} growMin @hide */
10155 /** @cfg {Number} growMax @hide */
10161 autoSize: Roo.emptyFn,
10165 deferHeight : true,
10168 actionMode : 'wrap',
10173 getAutoCreate : function(){
10175 var align = this.labelAlign || this.parentLabelAlign();
10180 cls: 'form-group' //input-group
10187 type : this.inputType,
10188 cls : 'form-control',
10189 autocomplete: 'new-password',
10190 placeholder : this.placeholder || ''
10194 input.name = this.name;
10197 input.cls += ' input-' + this.size;
10200 if (this.disabled) {
10201 input.disabled=true;
10204 var inputblock = input;
10206 if(this.hasFeedback && !this.allowBlank){
10210 cls: 'glyphicon form-control-feedback'
10213 if(this.removable && !this.editable && !this.tickable){
10215 cls : 'has-feedback',
10221 cls : 'roo-combo-removable-btn close'
10228 cls : 'has-feedback',
10237 if(this.removable && !this.editable && !this.tickable){
10239 cls : 'roo-removable',
10245 cls : 'roo-combo-removable-btn close'
10252 if (this.before || this.after) {
10255 cls : 'input-group',
10259 inputblock.cn.push({
10261 cls : 'input-group-addon',
10266 inputblock.cn.push(input);
10268 if(this.hasFeedback && !this.allowBlank){
10269 inputblock.cls += ' has-feedback';
10270 inputblock.cn.push(feedback);
10274 inputblock.cn.push({
10276 cls : 'input-group-addon',
10289 cls: 'form-hidden-field'
10303 cls: 'form-hidden-field'
10307 cls: 'roo-select2-choices',
10311 cls: 'roo-select2-search-field',
10324 cls: 'roo-select2-container input-group',
10329 // cls: 'typeahead typeahead-long dropdown-menu',
10330 // style: 'display:none'
10335 if(!this.multiple && this.showToggleBtn){
10341 if (this.caret != false) {
10344 cls: 'fa fa-' + this.caret
10351 cls : 'input-group-addon btn dropdown-toggle',
10356 cls: 'combobox-clear',
10370 combobox.cls += ' roo-select2-container-multi';
10373 if (align ==='left' && this.fieldLabel.length) {
10375 cfg.cls += ' roo-form-group-label-left';
10380 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10381 tooltip : 'This field is required'
10386 cls : 'control-label',
10387 html : this.fieldLabel
10399 var labelCfg = cfg.cn[1];
10400 var contentCfg = cfg.cn[2];
10402 if(this.indicatorpos == 'right'){
10407 cls : 'control-label',
10411 html : this.fieldLabel
10415 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10416 tooltip : 'This field is required'
10429 labelCfg = cfg.cn[0];
10430 contentCfg = cfg.cn[1];
10433 if(this.labelWidth > 12){
10434 labelCfg.style = "width: " + this.labelWidth + 'px';
10437 if(this.labelWidth < 13 && this.labelmd == 0){
10438 this.labelmd = this.labelWidth;
10441 if(this.labellg > 0){
10442 labelCfg.cls += ' col-lg-' + this.labellg;
10443 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10446 if(this.labelmd > 0){
10447 labelCfg.cls += ' col-md-' + this.labelmd;
10448 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10451 if(this.labelsm > 0){
10452 labelCfg.cls += ' col-sm-' + this.labelsm;
10453 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10456 if(this.labelxs > 0){
10457 labelCfg.cls += ' col-xs-' + this.labelxs;
10458 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10461 } else if ( this.fieldLabel.length) {
10462 // Roo.log(" label");
10466 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10467 tooltip : 'This field is required'
10471 //cls : 'input-group-addon',
10472 html : this.fieldLabel
10480 if(this.indicatorpos == 'right'){
10488 html : this.fieldLabel
10492 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10493 tooltip : 'This field is required'
10506 // Roo.log(" no label && no align");
10513 ['xs','sm','md','lg'].map(function(size){
10514 if (settings[size]) {
10515 cfg.cls += ' col-' + size + '-' + settings[size];
10526 onResize : function(w, h){
10527 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10528 // if(typeof w == 'number'){
10529 // var x = w - this.trigger.getWidth();
10530 // this.inputEl().setWidth(this.adjustWidth('input', x));
10531 // this.trigger.setStyle('left', x+'px');
10536 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10539 getResizeEl : function(){
10540 return this.inputEl();
10544 getPositionEl : function(){
10545 return this.inputEl();
10549 alignErrorIcon : function(){
10550 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10554 initEvents : function(){
10558 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10559 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10560 if(!this.multiple && this.showToggleBtn){
10561 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10562 if(this.hideTrigger){
10563 this.trigger.setDisplayed(false);
10565 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10569 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10572 if(this.removable && !this.editable && !this.tickable){
10573 var close = this.closeTriggerEl();
10576 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10577 close.on('click', this.removeBtnClick, this, close);
10581 //this.trigger.addClassOnOver('x-form-trigger-over');
10582 //this.trigger.addClassOnClick('x-form-trigger-click');
10585 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10589 closeTriggerEl : function()
10591 var close = this.el.select('.roo-combo-removable-btn', true).first();
10592 return close ? close : false;
10595 removeBtnClick : function(e, h, el)
10597 e.preventDefault();
10599 if(this.fireEvent("remove", this) !== false){
10601 this.fireEvent("afterremove", this)
10605 createList : function()
10607 this.list = Roo.get(document.body).createChild({
10609 cls: 'typeahead typeahead-long dropdown-menu',
10610 style: 'display:none'
10613 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10618 initTrigger : function(){
10623 onDestroy : function(){
10625 this.trigger.removeAllListeners();
10626 // this.trigger.remove();
10629 // this.wrap.remove();
10631 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10635 onFocus : function(){
10636 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10638 if(!this.mimicing){
10639 this.wrap.addClass('x-trigger-wrap-focus');
10640 this.mimicing = true;
10641 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10642 if(this.monitorTab){
10643 this.el.on("keydown", this.checkTab, this);
10650 checkTab : function(e){
10651 if(e.getKey() == e.TAB){
10652 this.triggerBlur();
10657 onBlur : function(){
10662 mimicBlur : function(e, t){
10664 if(!this.wrap.contains(t) && this.validateBlur()){
10665 this.triggerBlur();
10671 triggerBlur : function(){
10672 this.mimicing = false;
10673 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10674 if(this.monitorTab){
10675 this.el.un("keydown", this.checkTab, this);
10677 //this.wrap.removeClass('x-trigger-wrap-focus');
10678 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10682 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10683 validateBlur : function(e, t){
10688 onDisable : function(){
10689 this.inputEl().dom.disabled = true;
10690 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10692 // this.wrap.addClass('x-item-disabled');
10697 onEnable : function(){
10698 this.inputEl().dom.disabled = false;
10699 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10701 // this.el.removeClass('x-item-disabled');
10706 onShow : function(){
10707 var ae = this.getActionEl();
10710 ae.dom.style.display = '';
10711 ae.dom.style.visibility = 'visible';
10717 onHide : function(){
10718 var ae = this.getActionEl();
10719 ae.dom.style.display = 'none';
10723 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10724 * by an implementing function.
10726 * @param {EventObject} e
10728 onTriggerClick : Roo.emptyFn
10732 * Ext JS Library 1.1.1
10733 * Copyright(c) 2006-2007, Ext JS, LLC.
10735 * Originally Released Under LGPL - original licence link has changed is not relivant.
10738 * <script type="text/javascript">
10743 * @class Roo.data.SortTypes
10745 * Defines the default sorting (casting?) comparison functions used when sorting data.
10747 Roo.data.SortTypes = {
10749 * Default sort that does nothing
10750 * @param {Mixed} s The value being converted
10751 * @return {Mixed} The comparison value
10753 none : function(s){
10758 * The regular expression used to strip tags
10762 stripTagsRE : /<\/?[^>]+>/gi,
10765 * Strips all HTML tags to sort on text only
10766 * @param {Mixed} s The value being converted
10767 * @return {String} The comparison value
10769 asText : function(s){
10770 return String(s).replace(this.stripTagsRE, "");
10774 * Strips all HTML tags to sort on text only - Case insensitive
10775 * @param {Mixed} s The value being converted
10776 * @return {String} The comparison value
10778 asUCText : function(s){
10779 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10783 * Case insensitive string
10784 * @param {Mixed} s The value being converted
10785 * @return {String} The comparison value
10787 asUCString : function(s) {
10788 return String(s).toUpperCase();
10793 * @param {Mixed} s The value being converted
10794 * @return {Number} The comparison value
10796 asDate : function(s) {
10800 if(s instanceof Date){
10801 return s.getTime();
10803 return Date.parse(String(s));
10808 * @param {Mixed} s The value being converted
10809 * @return {Float} The comparison value
10811 asFloat : function(s) {
10812 var val = parseFloat(String(s).replace(/,/g, ""));
10821 * @param {Mixed} s The value being converted
10822 * @return {Number} The comparison value
10824 asInt : function(s) {
10825 var val = parseInt(String(s).replace(/,/g, ""));
10833 * Ext JS Library 1.1.1
10834 * Copyright(c) 2006-2007, Ext JS, LLC.
10836 * Originally Released Under LGPL - original licence link has changed is not relivant.
10839 * <script type="text/javascript">
10843 * @class Roo.data.Record
10844 * Instances of this class encapsulate both record <em>definition</em> information, and record
10845 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10846 * to access Records cached in an {@link Roo.data.Store} object.<br>
10848 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10849 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10852 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10854 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10855 * {@link #create}. The parameters are the same.
10856 * @param {Array} data An associative Array of data values keyed by the field name.
10857 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10858 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10859 * not specified an integer id is generated.
10861 Roo.data.Record = function(data, id){
10862 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10867 * Generate a constructor for a specific record layout.
10868 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10869 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10870 * Each field definition object may contain the following properties: <ul>
10871 * <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,
10872 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10873 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10874 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10875 * is being used, then this is a string containing the javascript expression to reference the data relative to
10876 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10877 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10878 * this may be omitted.</p></li>
10879 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10880 * <ul><li>auto (Default, implies no conversion)</li>
10885 * <li>date</li></ul></p></li>
10886 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10887 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10888 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10889 * by the Reader into an object that will be stored in the Record. It is passed the
10890 * following parameters:<ul>
10891 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10893 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10895 * <br>usage:<br><pre><code>
10896 var TopicRecord = Roo.data.Record.create(
10897 {name: 'title', mapping: 'topic_title'},
10898 {name: 'author', mapping: 'username'},
10899 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10900 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10901 {name: 'lastPoster', mapping: 'user2'},
10902 {name: 'excerpt', mapping: 'post_text'}
10905 var myNewRecord = new TopicRecord({
10906 title: 'Do my job please',
10909 lastPost: new Date(),
10910 lastPoster: 'Animal',
10911 excerpt: 'No way dude!'
10913 myStore.add(myNewRecord);
10918 Roo.data.Record.create = function(o){
10919 var f = function(){
10920 f.superclass.constructor.apply(this, arguments);
10922 Roo.extend(f, Roo.data.Record);
10923 var p = f.prototype;
10924 p.fields = new Roo.util.MixedCollection(false, function(field){
10927 for(var i = 0, len = o.length; i < len; i++){
10928 p.fields.add(new Roo.data.Field(o[i]));
10930 f.getField = function(name){
10931 return p.fields.get(name);
10936 Roo.data.Record.AUTO_ID = 1000;
10937 Roo.data.Record.EDIT = 'edit';
10938 Roo.data.Record.REJECT = 'reject';
10939 Roo.data.Record.COMMIT = 'commit';
10941 Roo.data.Record.prototype = {
10943 * Readonly flag - true if this record has been modified.
10952 join : function(store){
10953 this.store = store;
10957 * Set the named field to the specified value.
10958 * @param {String} name The name of the field to set.
10959 * @param {Object} value The value to set the field to.
10961 set : function(name, value){
10962 if(this.data[name] == value){
10966 if(!this.modified){
10967 this.modified = {};
10969 if(typeof this.modified[name] == 'undefined'){
10970 this.modified[name] = this.data[name];
10972 this.data[name] = value;
10973 if(!this.editing && this.store){
10974 this.store.afterEdit(this);
10979 * Get the value of the named field.
10980 * @param {String} name The name of the field to get the value of.
10981 * @return {Object} The value of the field.
10983 get : function(name){
10984 return this.data[name];
10988 beginEdit : function(){
10989 this.editing = true;
10990 this.modified = {};
10994 cancelEdit : function(){
10995 this.editing = false;
10996 delete this.modified;
11000 endEdit : function(){
11001 this.editing = false;
11002 if(this.dirty && this.store){
11003 this.store.afterEdit(this);
11008 * Usually called by the {@link Roo.data.Store} which owns the Record.
11009 * Rejects all changes made to the Record since either creation, or the last commit operation.
11010 * Modified fields are reverted to their original values.
11012 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11013 * of reject operations.
11015 reject : function(){
11016 var m = this.modified;
11018 if(typeof m[n] != "function"){
11019 this.data[n] = m[n];
11022 this.dirty = false;
11023 delete this.modified;
11024 this.editing = false;
11026 this.store.afterReject(this);
11031 * Usually called by the {@link Roo.data.Store} which owns the Record.
11032 * Commits all changes made to the Record since either creation, or the last commit operation.
11034 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11035 * of commit operations.
11037 commit : function(){
11038 this.dirty = false;
11039 delete this.modified;
11040 this.editing = false;
11042 this.store.afterCommit(this);
11047 hasError : function(){
11048 return this.error != null;
11052 clearError : function(){
11057 * Creates a copy of this record.
11058 * @param {String} id (optional) A new record id if you don't want to use this record's id
11061 copy : function(newId) {
11062 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11066 * Ext JS Library 1.1.1
11067 * Copyright(c) 2006-2007, Ext JS, LLC.
11069 * Originally Released Under LGPL - original licence link has changed is not relivant.
11072 * <script type="text/javascript">
11078 * @class Roo.data.Store
11079 * @extends Roo.util.Observable
11080 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11081 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11083 * 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
11084 * has no knowledge of the format of the data returned by the Proxy.<br>
11086 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11087 * instances from the data object. These records are cached and made available through accessor functions.
11089 * Creates a new Store.
11090 * @param {Object} config A config object containing the objects needed for the Store to access data,
11091 * and read the data into Records.
11093 Roo.data.Store = function(config){
11094 this.data = new Roo.util.MixedCollection(false);
11095 this.data.getKey = function(o){
11098 this.baseParams = {};
11100 this.paramNames = {
11105 "multisort" : "_multisort"
11108 if(config && config.data){
11109 this.inlineData = config.data;
11110 delete config.data;
11113 Roo.apply(this, config);
11115 if(this.reader){ // reader passed
11116 this.reader = Roo.factory(this.reader, Roo.data);
11117 this.reader.xmodule = this.xmodule || false;
11118 if(!this.recordType){
11119 this.recordType = this.reader.recordType;
11121 if(this.reader.onMetaChange){
11122 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11126 if(this.recordType){
11127 this.fields = this.recordType.prototype.fields;
11129 this.modified = [];
11133 * @event datachanged
11134 * Fires when the data cache has changed, and a widget which is using this Store
11135 * as a Record cache should refresh its view.
11136 * @param {Store} this
11138 datachanged : true,
11140 * @event metachange
11141 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11142 * @param {Store} this
11143 * @param {Object} meta The JSON metadata
11148 * Fires when Records have been added to the Store
11149 * @param {Store} this
11150 * @param {Roo.data.Record[]} records The array of Records added
11151 * @param {Number} index The index at which the record(s) were added
11156 * Fires when a Record has been removed from the Store
11157 * @param {Store} this
11158 * @param {Roo.data.Record} record The Record that was removed
11159 * @param {Number} index The index at which the record was removed
11164 * Fires when a Record has been updated
11165 * @param {Store} this
11166 * @param {Roo.data.Record} record The Record that was updated
11167 * @param {String} operation The update operation being performed. Value may be one of:
11169 Roo.data.Record.EDIT
11170 Roo.data.Record.REJECT
11171 Roo.data.Record.COMMIT
11177 * Fires when the data cache has been cleared.
11178 * @param {Store} this
11182 * @event beforeload
11183 * Fires before a request is made for a new data object. If the beforeload handler returns false
11184 * the load action will be canceled.
11185 * @param {Store} this
11186 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11190 * @event beforeloadadd
11191 * Fires after a new set of Records has been loaded.
11192 * @param {Store} this
11193 * @param {Roo.data.Record[]} records The Records that were loaded
11194 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11196 beforeloadadd : true,
11199 * Fires after a new set of Records has been loaded, before they are added to the store.
11200 * @param {Store} this
11201 * @param {Roo.data.Record[]} records The Records that were loaded
11202 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11203 * @params {Object} return from reader
11207 * @event loadexception
11208 * Fires if an exception occurs in the Proxy during loading.
11209 * Called with the signature of the Proxy's "loadexception" event.
11210 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11213 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11214 * @param {Object} load options
11215 * @param {Object} jsonData from your request (normally this contains the Exception)
11217 loadexception : true
11221 this.proxy = Roo.factory(this.proxy, Roo.data);
11222 this.proxy.xmodule = this.xmodule || false;
11223 this.relayEvents(this.proxy, ["loadexception"]);
11225 this.sortToggle = {};
11226 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11228 Roo.data.Store.superclass.constructor.call(this);
11230 if(this.inlineData){
11231 this.loadData(this.inlineData);
11232 delete this.inlineData;
11236 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11238 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11239 * without a remote query - used by combo/forms at present.
11243 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11246 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11249 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11250 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11253 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11254 * on any HTTP request
11257 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11260 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11264 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11265 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11267 remoteSort : false,
11270 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11271 * loaded or when a record is removed. (defaults to false).
11273 pruneModifiedRecords : false,
11276 lastOptions : null,
11279 * Add Records to the Store and fires the add event.
11280 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11282 add : function(records){
11283 records = [].concat(records);
11284 for(var i = 0, len = records.length; i < len; i++){
11285 records[i].join(this);
11287 var index = this.data.length;
11288 this.data.addAll(records);
11289 this.fireEvent("add", this, records, index);
11293 * Remove a Record from the Store and fires the remove event.
11294 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11296 remove : function(record){
11297 var index = this.data.indexOf(record);
11298 this.data.removeAt(index);
11300 if(this.pruneModifiedRecords){
11301 this.modified.remove(record);
11303 this.fireEvent("remove", this, record, index);
11307 * Remove all Records from the Store and fires the clear event.
11309 removeAll : function(){
11311 if(this.pruneModifiedRecords){
11312 this.modified = [];
11314 this.fireEvent("clear", this);
11318 * Inserts Records to the Store at the given index and fires the add event.
11319 * @param {Number} index The start index at which to insert the passed Records.
11320 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11322 insert : function(index, records){
11323 records = [].concat(records);
11324 for(var i = 0, len = records.length; i < len; i++){
11325 this.data.insert(index, records[i]);
11326 records[i].join(this);
11328 this.fireEvent("add", this, records, index);
11332 * Get the index within the cache of the passed Record.
11333 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11334 * @return {Number} The index of the passed Record. Returns -1 if not found.
11336 indexOf : function(record){
11337 return this.data.indexOf(record);
11341 * Get the index within the cache of the Record with the passed id.
11342 * @param {String} id The id of the Record to find.
11343 * @return {Number} The index of the Record. Returns -1 if not found.
11345 indexOfId : function(id){
11346 return this.data.indexOfKey(id);
11350 * Get the Record with the specified id.
11351 * @param {String} id The id of the Record to find.
11352 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11354 getById : function(id){
11355 return this.data.key(id);
11359 * Get the Record at the specified index.
11360 * @param {Number} index The index of the Record to find.
11361 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11363 getAt : function(index){
11364 return this.data.itemAt(index);
11368 * Returns a range of Records between specified indices.
11369 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11370 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11371 * @return {Roo.data.Record[]} An array of Records
11373 getRange : function(start, end){
11374 return this.data.getRange(start, end);
11378 storeOptions : function(o){
11379 o = Roo.apply({}, o);
11382 this.lastOptions = o;
11386 * Loads the Record cache from the configured Proxy using the configured Reader.
11388 * If using remote paging, then the first load call must specify the <em>start</em>
11389 * and <em>limit</em> properties in the options.params property to establish the initial
11390 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11392 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11393 * and this call will return before the new data has been loaded. Perform any post-processing
11394 * in a callback function, or in a "load" event handler.</strong>
11396 * @param {Object} options An object containing properties which control loading options:<ul>
11397 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11398 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11399 * passed the following arguments:<ul>
11400 * <li>r : Roo.data.Record[]</li>
11401 * <li>options: Options object from the load call</li>
11402 * <li>success: Boolean success indicator</li></ul></li>
11403 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11404 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11407 load : function(options){
11408 options = options || {};
11409 if(this.fireEvent("beforeload", this, options) !== false){
11410 this.storeOptions(options);
11411 var p = Roo.apply(options.params || {}, this.baseParams);
11412 // if meta was not loaded from remote source.. try requesting it.
11413 if (!this.reader.metaFromRemote) {
11414 p._requestMeta = 1;
11416 if(this.sortInfo && this.remoteSort){
11417 var pn = this.paramNames;
11418 p[pn["sort"]] = this.sortInfo.field;
11419 p[pn["dir"]] = this.sortInfo.direction;
11421 if (this.multiSort) {
11422 var pn = this.paramNames;
11423 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11426 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11431 * Reloads the Record cache from the configured Proxy using the configured Reader and
11432 * the options from the last load operation performed.
11433 * @param {Object} options (optional) An object containing properties which may override the options
11434 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11435 * the most recently used options are reused).
11437 reload : function(options){
11438 this.load(Roo.applyIf(options||{}, this.lastOptions));
11442 // Called as a callback by the Reader during a load operation.
11443 loadRecords : function(o, options, success){
11444 if(!o || success === false){
11445 if(success !== false){
11446 this.fireEvent("load", this, [], options, o);
11448 if(options.callback){
11449 options.callback.call(options.scope || this, [], options, false);
11453 // if data returned failure - throw an exception.
11454 if (o.success === false) {
11455 // show a message if no listener is registered.
11456 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11457 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11459 // loadmask wil be hooked into this..
11460 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11463 var r = o.records, t = o.totalRecords || r.length;
11465 this.fireEvent("beforeloadadd", this, r, options, o);
11467 if(!options || options.add !== true){
11468 if(this.pruneModifiedRecords){
11469 this.modified = [];
11471 for(var i = 0, len = r.length; i < len; i++){
11475 this.data = this.snapshot;
11476 delete this.snapshot;
11479 this.data.addAll(r);
11480 this.totalLength = t;
11482 this.fireEvent("datachanged", this);
11484 this.totalLength = Math.max(t, this.data.length+r.length);
11488 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11490 var e = new Roo.data.Record({});
11492 e.set(this.parent.displayField, this.parent.emptyTitle);
11493 e.set(this.parent.valueField, '');
11498 this.fireEvent("load", this, r, options, o);
11499 if(options.callback){
11500 options.callback.call(options.scope || this, r, options, true);
11506 * Loads data from a passed data block. A Reader which understands the format of the data
11507 * must have been configured in the constructor.
11508 * @param {Object} data The data block from which to read the Records. The format of the data expected
11509 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11510 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11512 loadData : function(o, append){
11513 var r = this.reader.readRecords(o);
11514 this.loadRecords(r, {add: append}, true);
11518 * Gets the number of cached records.
11520 * <em>If using paging, this may not be the total size of the dataset. If the data object
11521 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11522 * the data set size</em>
11524 getCount : function(){
11525 return this.data.length || 0;
11529 * Gets the total number of records in the dataset as returned by the server.
11531 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11532 * the dataset size</em>
11534 getTotalCount : function(){
11535 return this.totalLength || 0;
11539 * Returns the sort state of the Store as an object with two properties:
11541 field {String} The name of the field by which the Records are sorted
11542 direction {String} The sort order, "ASC" or "DESC"
11545 getSortState : function(){
11546 return this.sortInfo;
11550 applySort : function(){
11551 if(this.sortInfo && !this.remoteSort){
11552 var s = this.sortInfo, f = s.field;
11553 var st = this.fields.get(f).sortType;
11554 var fn = function(r1, r2){
11555 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11556 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11558 this.data.sort(s.direction, fn);
11559 if(this.snapshot && this.snapshot != this.data){
11560 this.snapshot.sort(s.direction, fn);
11566 * Sets the default sort column and order to be used by the next load operation.
11567 * @param {String} fieldName The name of the field to sort by.
11568 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11570 setDefaultSort : function(field, dir){
11571 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11575 * Sort the Records.
11576 * If remote sorting is used, the sort is performed on the server, and the cache is
11577 * reloaded. If local sorting is used, the cache is sorted internally.
11578 * @param {String} fieldName The name of the field to sort by.
11579 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11581 sort : function(fieldName, dir){
11582 var f = this.fields.get(fieldName);
11584 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11586 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11587 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11592 this.sortToggle[f.name] = dir;
11593 this.sortInfo = {field: f.name, direction: dir};
11594 if(!this.remoteSort){
11596 this.fireEvent("datachanged", this);
11598 this.load(this.lastOptions);
11603 * Calls the specified function for each of the Records in the cache.
11604 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11605 * Returning <em>false</em> aborts and exits the iteration.
11606 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11608 each : function(fn, scope){
11609 this.data.each(fn, scope);
11613 * Gets all records modified since the last commit. Modified records are persisted across load operations
11614 * (e.g., during paging).
11615 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11617 getModifiedRecords : function(){
11618 return this.modified;
11622 createFilterFn : function(property, value, anyMatch){
11623 if(!value.exec){ // not a regex
11624 value = String(value);
11625 if(value.length == 0){
11628 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11630 return function(r){
11631 return value.test(r.data[property]);
11636 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11637 * @param {String} property A field on your records
11638 * @param {Number} start The record index to start at (defaults to 0)
11639 * @param {Number} end The last record index to include (defaults to length - 1)
11640 * @return {Number} The sum
11642 sum : function(property, start, end){
11643 var rs = this.data.items, v = 0;
11644 start = start || 0;
11645 end = (end || end === 0) ? end : rs.length-1;
11647 for(var i = start; i <= end; i++){
11648 v += (rs[i].data[property] || 0);
11654 * Filter the records by a specified property.
11655 * @param {String} field A field on your records
11656 * @param {String/RegExp} value Either a string that the field
11657 * should start with or a RegExp to test against the field
11658 * @param {Boolean} anyMatch True to match any part not just the beginning
11660 filter : function(property, value, anyMatch){
11661 var fn = this.createFilterFn(property, value, anyMatch);
11662 return fn ? this.filterBy(fn) : this.clearFilter();
11666 * Filter by a function. The specified function will be called with each
11667 * record in this data source. If the function returns true the record is included,
11668 * otherwise it is filtered.
11669 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11670 * @param {Object} scope (optional) The scope of the function (defaults to this)
11672 filterBy : function(fn, scope){
11673 this.snapshot = this.snapshot || this.data;
11674 this.data = this.queryBy(fn, scope||this);
11675 this.fireEvent("datachanged", this);
11679 * Query the records by a specified property.
11680 * @param {String} field A field on your records
11681 * @param {String/RegExp} value Either a string that the field
11682 * should start with or a RegExp to test against the field
11683 * @param {Boolean} anyMatch True to match any part not just the beginning
11684 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11686 query : function(property, value, anyMatch){
11687 var fn = this.createFilterFn(property, value, anyMatch);
11688 return fn ? this.queryBy(fn) : this.data.clone();
11692 * Query by a function. The specified function will be called with each
11693 * record in this data source. If the function returns true the record is included
11695 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11696 * @param {Object} scope (optional) The scope of the function (defaults to this)
11697 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11699 queryBy : function(fn, scope){
11700 var data = this.snapshot || this.data;
11701 return data.filterBy(fn, scope||this);
11705 * Collects unique values for a particular dataIndex from this store.
11706 * @param {String} dataIndex The property to collect
11707 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11708 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11709 * @return {Array} An array of the unique values
11711 collect : function(dataIndex, allowNull, bypassFilter){
11712 var d = (bypassFilter === true && this.snapshot) ?
11713 this.snapshot.items : this.data.items;
11714 var v, sv, r = [], l = {};
11715 for(var i = 0, len = d.length; i < len; i++){
11716 v = d[i].data[dataIndex];
11718 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11727 * Revert to a view of the Record cache with no filtering applied.
11728 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11730 clearFilter : function(suppressEvent){
11731 if(this.snapshot && this.snapshot != this.data){
11732 this.data = this.snapshot;
11733 delete this.snapshot;
11734 if(suppressEvent !== true){
11735 this.fireEvent("datachanged", this);
11741 afterEdit : function(record){
11742 if(this.modified.indexOf(record) == -1){
11743 this.modified.push(record);
11745 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11749 afterReject : function(record){
11750 this.modified.remove(record);
11751 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11755 afterCommit : function(record){
11756 this.modified.remove(record);
11757 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11761 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11762 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11764 commitChanges : function(){
11765 var m = this.modified.slice(0);
11766 this.modified = [];
11767 for(var i = 0, len = m.length; i < len; i++){
11773 * Cancel outstanding changes on all changed records.
11775 rejectChanges : function(){
11776 var m = this.modified.slice(0);
11777 this.modified = [];
11778 for(var i = 0, len = m.length; i < len; i++){
11783 onMetaChange : function(meta, rtype, o){
11784 this.recordType = rtype;
11785 this.fields = rtype.prototype.fields;
11786 delete this.snapshot;
11787 this.sortInfo = meta.sortInfo || this.sortInfo;
11788 this.modified = [];
11789 this.fireEvent('metachange', this, this.reader.meta);
11792 moveIndex : function(data, type)
11794 var index = this.indexOf(data);
11796 var newIndex = index + type;
11800 this.insert(newIndex, data);
11805 * Ext JS Library 1.1.1
11806 * Copyright(c) 2006-2007, Ext JS, LLC.
11808 * Originally Released Under LGPL - original licence link has changed is not relivant.
11811 * <script type="text/javascript">
11815 * @class Roo.data.SimpleStore
11816 * @extends Roo.data.Store
11817 * Small helper class to make creating Stores from Array data easier.
11818 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11819 * @cfg {Array} fields An array of field definition objects, or field name strings.
11820 * @cfg {Array} data The multi-dimensional array of data
11822 * @param {Object} config
11824 Roo.data.SimpleStore = function(config){
11825 Roo.data.SimpleStore.superclass.constructor.call(this, {
11827 reader: new Roo.data.ArrayReader({
11830 Roo.data.Record.create(config.fields)
11832 proxy : new Roo.data.MemoryProxy(config.data)
11836 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11838 * Ext JS Library 1.1.1
11839 * Copyright(c) 2006-2007, Ext JS, LLC.
11841 * Originally Released Under LGPL - original licence link has changed is not relivant.
11844 * <script type="text/javascript">
11849 * @extends Roo.data.Store
11850 * @class Roo.data.JsonStore
11851 * Small helper class to make creating Stores for JSON data easier. <br/>
11853 var store = new Roo.data.JsonStore({
11854 url: 'get-images.php',
11856 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11859 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11860 * JsonReader and HttpProxy (unless inline data is provided).</b>
11861 * @cfg {Array} fields An array of field definition objects, or field name strings.
11863 * @param {Object} config
11865 Roo.data.JsonStore = function(c){
11866 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11867 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11868 reader: new Roo.data.JsonReader(c, c.fields)
11871 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11873 * Ext JS Library 1.1.1
11874 * Copyright(c) 2006-2007, Ext JS, LLC.
11876 * Originally Released Under LGPL - original licence link has changed is not relivant.
11879 * <script type="text/javascript">
11883 Roo.data.Field = function(config){
11884 if(typeof config == "string"){
11885 config = {name: config};
11887 Roo.apply(this, config);
11890 this.type = "auto";
11893 var st = Roo.data.SortTypes;
11894 // named sortTypes are supported, here we look them up
11895 if(typeof this.sortType == "string"){
11896 this.sortType = st[this.sortType];
11899 // set default sortType for strings and dates
11900 if(!this.sortType){
11903 this.sortType = st.asUCString;
11906 this.sortType = st.asDate;
11909 this.sortType = st.none;
11914 var stripRe = /[\$,%]/g;
11916 // prebuilt conversion function for this field, instead of
11917 // switching every time we're reading a value
11919 var cv, dateFormat = this.dateFormat;
11924 cv = function(v){ return v; };
11927 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11931 return v !== undefined && v !== null && v !== '' ?
11932 parseInt(String(v).replace(stripRe, ""), 10) : '';
11937 return v !== undefined && v !== null && v !== '' ?
11938 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11943 cv = function(v){ return v === true || v === "true" || v == 1; };
11950 if(v instanceof Date){
11954 if(dateFormat == "timestamp"){
11955 return new Date(v*1000);
11957 return Date.parseDate(v, dateFormat);
11959 var parsed = Date.parse(v);
11960 return parsed ? new Date(parsed) : null;
11969 Roo.data.Field.prototype = {
11977 * Ext JS Library 1.1.1
11978 * Copyright(c) 2006-2007, Ext JS, LLC.
11980 * Originally Released Under LGPL - original licence link has changed is not relivant.
11983 * <script type="text/javascript">
11986 // Base class for reading structured data from a data source. This class is intended to be
11987 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11990 * @class Roo.data.DataReader
11991 * Base class for reading structured data from a data source. This class is intended to be
11992 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11995 Roo.data.DataReader = function(meta, recordType){
11999 this.recordType = recordType instanceof Array ?
12000 Roo.data.Record.create(recordType) : recordType;
12003 Roo.data.DataReader.prototype = {
12005 * Create an empty record
12006 * @param {Object} data (optional) - overlay some values
12007 * @return {Roo.data.Record} record created.
12009 newRow : function(d) {
12011 this.recordType.prototype.fields.each(function(c) {
12013 case 'int' : da[c.name] = 0; break;
12014 case 'date' : da[c.name] = new Date(); break;
12015 case 'float' : da[c.name] = 0.0; break;
12016 case 'boolean' : da[c.name] = false; break;
12017 default : da[c.name] = ""; break;
12021 return new this.recordType(Roo.apply(da, d));
12026 * Ext JS Library 1.1.1
12027 * Copyright(c) 2006-2007, Ext JS, LLC.
12029 * Originally Released Under LGPL - original licence link has changed is not relivant.
12032 * <script type="text/javascript">
12036 * @class Roo.data.DataProxy
12037 * @extends Roo.data.Observable
12038 * This class is an abstract base class for implementations which provide retrieval of
12039 * unformatted data objects.<br>
12041 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12042 * (of the appropriate type which knows how to parse the data object) to provide a block of
12043 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12045 * Custom implementations must implement the load method as described in
12046 * {@link Roo.data.HttpProxy#load}.
12048 Roo.data.DataProxy = function(){
12051 * @event beforeload
12052 * Fires before a network request is made to retrieve a data object.
12053 * @param {Object} This DataProxy object.
12054 * @param {Object} params The params parameter to the load function.
12059 * Fires before the load method's callback is called.
12060 * @param {Object} This DataProxy object.
12061 * @param {Object} o The data object.
12062 * @param {Object} arg The callback argument object passed to the load function.
12066 * @event loadexception
12067 * Fires if an Exception occurs during data retrieval.
12068 * @param {Object} This DataProxy object.
12069 * @param {Object} o The data object.
12070 * @param {Object} arg The callback argument object passed to the load function.
12071 * @param {Object} e The Exception.
12073 loadexception : true
12075 Roo.data.DataProxy.superclass.constructor.call(this);
12078 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12081 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12085 * Ext JS Library 1.1.1
12086 * Copyright(c) 2006-2007, Ext JS, LLC.
12088 * Originally Released Under LGPL - original licence link has changed is not relivant.
12091 * <script type="text/javascript">
12094 * @class Roo.data.MemoryProxy
12095 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12096 * to the Reader when its load method is called.
12098 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12100 Roo.data.MemoryProxy = function(data){
12104 Roo.data.MemoryProxy.superclass.constructor.call(this);
12108 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12111 * Load data from the requested source (in this case an in-memory
12112 * data object passed to the constructor), read the data object into
12113 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12114 * process that block using the passed callback.
12115 * @param {Object} params This parameter is not used by the MemoryProxy class.
12116 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12117 * object into a block of Roo.data.Records.
12118 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12119 * The function must be passed <ul>
12120 * <li>The Record block object</li>
12121 * <li>The "arg" argument from the load function</li>
12122 * <li>A boolean success indicator</li>
12124 * @param {Object} scope The scope in which to call the callback
12125 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12127 load : function(params, reader, callback, scope, arg){
12128 params = params || {};
12131 result = reader.readRecords(this.data);
12133 this.fireEvent("loadexception", this, arg, null, e);
12134 callback.call(scope, null, arg, false);
12137 callback.call(scope, result, arg, true);
12141 update : function(params, records){
12146 * Ext JS Library 1.1.1
12147 * Copyright(c) 2006-2007, Ext JS, LLC.
12149 * Originally Released Under LGPL - original licence link has changed is not relivant.
12152 * <script type="text/javascript">
12155 * @class Roo.data.HttpProxy
12156 * @extends Roo.data.DataProxy
12157 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12158 * configured to reference a certain URL.<br><br>
12160 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12161 * from which the running page was served.<br><br>
12163 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12165 * Be aware that to enable the browser to parse an XML document, the server must set
12166 * the Content-Type header in the HTTP response to "text/xml".
12168 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12169 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12170 * will be used to make the request.
12172 Roo.data.HttpProxy = function(conn){
12173 Roo.data.HttpProxy.superclass.constructor.call(this);
12174 // is conn a conn config or a real conn?
12176 this.useAjax = !conn || !conn.events;
12180 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12181 // thse are take from connection...
12184 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12187 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12188 * extra parameters to each request made by this object. (defaults to undefined)
12191 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12192 * to each request made by this object. (defaults to undefined)
12195 * @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)
12198 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12201 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12207 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12211 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12212 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12213 * a finer-grained basis than the DataProxy events.
12215 getConnection : function(){
12216 return this.useAjax ? Roo.Ajax : this.conn;
12220 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12221 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12222 * process that block using the passed callback.
12223 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12224 * for the request to the remote server.
12225 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12226 * object into a block of Roo.data.Records.
12227 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12228 * The function must be passed <ul>
12229 * <li>The Record block object</li>
12230 * <li>The "arg" argument from the load function</li>
12231 * <li>A boolean success indicator</li>
12233 * @param {Object} scope The scope in which to call the callback
12234 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12236 load : function(params, reader, callback, scope, arg){
12237 if(this.fireEvent("beforeload", this, params) !== false){
12239 params : params || {},
12241 callback : callback,
12246 callback : this.loadResponse,
12250 Roo.applyIf(o, this.conn);
12251 if(this.activeRequest){
12252 Roo.Ajax.abort(this.activeRequest);
12254 this.activeRequest = Roo.Ajax.request(o);
12256 this.conn.request(o);
12259 callback.call(scope||this, null, arg, false);
12264 loadResponse : function(o, success, response){
12265 delete this.activeRequest;
12267 this.fireEvent("loadexception", this, o, response);
12268 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12273 result = o.reader.read(response);
12275 this.fireEvent("loadexception", this, o, response, e);
12276 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12280 this.fireEvent("load", this, o, o.request.arg);
12281 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12285 update : function(dataSet){
12290 updateResponse : function(dataSet){
12295 * Ext JS Library 1.1.1
12296 * Copyright(c) 2006-2007, Ext JS, LLC.
12298 * Originally Released Under LGPL - original licence link has changed is not relivant.
12301 * <script type="text/javascript">
12305 * @class Roo.data.ScriptTagProxy
12306 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12307 * other than the originating domain of the running page.<br><br>
12309 * <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
12310 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12312 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12313 * source code that is used as the source inside a <script> tag.<br><br>
12315 * In order for the browser to process the returned data, the server must wrap the data object
12316 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12317 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12318 * depending on whether the callback name was passed:
12321 boolean scriptTag = false;
12322 String cb = request.getParameter("callback");
12325 response.setContentType("text/javascript");
12327 response.setContentType("application/x-json");
12329 Writer out = response.getWriter();
12331 out.write(cb + "(");
12333 out.print(dataBlock.toJsonString());
12340 * @param {Object} config A configuration object.
12342 Roo.data.ScriptTagProxy = function(config){
12343 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12344 Roo.apply(this, config);
12345 this.head = document.getElementsByTagName("head")[0];
12348 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12350 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12352 * @cfg {String} url The URL from which to request the data object.
12355 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12359 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12360 * the server the name of the callback function set up by the load call to process the returned data object.
12361 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12362 * javascript output which calls this named function passing the data object as its only parameter.
12364 callbackParam : "callback",
12366 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12367 * name to the request.
12372 * Load data from the configured URL, read the data object into
12373 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12374 * process that block using the passed callback.
12375 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12376 * for the request to the remote server.
12377 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12378 * object into a block of Roo.data.Records.
12379 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12380 * The function must be passed <ul>
12381 * <li>The Record block object</li>
12382 * <li>The "arg" argument from the load function</li>
12383 * <li>A boolean success indicator</li>
12385 * @param {Object} scope The scope in which to call the callback
12386 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12388 load : function(params, reader, callback, scope, arg){
12389 if(this.fireEvent("beforeload", this, params) !== false){
12391 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12393 var url = this.url;
12394 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12396 url += "&_dc=" + (new Date().getTime());
12398 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12401 cb : "stcCallback"+transId,
12402 scriptId : "stcScript"+transId,
12406 callback : callback,
12412 window[trans.cb] = function(o){
12413 conn.handleResponse(o, trans);
12416 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12418 if(this.autoAbort !== false){
12422 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12424 var script = document.createElement("script");
12425 script.setAttribute("src", url);
12426 script.setAttribute("type", "text/javascript");
12427 script.setAttribute("id", trans.scriptId);
12428 this.head.appendChild(script);
12430 this.trans = trans;
12432 callback.call(scope||this, null, arg, false);
12437 isLoading : function(){
12438 return this.trans ? true : false;
12442 * Abort the current server request.
12444 abort : function(){
12445 if(this.isLoading()){
12446 this.destroyTrans(this.trans);
12451 destroyTrans : function(trans, isLoaded){
12452 this.head.removeChild(document.getElementById(trans.scriptId));
12453 clearTimeout(trans.timeoutId);
12455 window[trans.cb] = undefined;
12457 delete window[trans.cb];
12460 // if hasn't been loaded, wait for load to remove it to prevent script error
12461 window[trans.cb] = function(){
12462 window[trans.cb] = undefined;
12464 delete window[trans.cb];
12471 handleResponse : function(o, trans){
12472 this.trans = false;
12473 this.destroyTrans(trans, true);
12476 result = trans.reader.readRecords(o);
12478 this.fireEvent("loadexception", this, o, trans.arg, e);
12479 trans.callback.call(trans.scope||window, null, trans.arg, false);
12482 this.fireEvent("load", this, o, trans.arg);
12483 trans.callback.call(trans.scope||window, result, trans.arg, true);
12487 handleFailure : function(trans){
12488 this.trans = false;
12489 this.destroyTrans(trans, false);
12490 this.fireEvent("loadexception", this, null, trans.arg);
12491 trans.callback.call(trans.scope||window, null, trans.arg, false);
12495 * Ext JS Library 1.1.1
12496 * Copyright(c) 2006-2007, Ext JS, LLC.
12498 * Originally Released Under LGPL - original licence link has changed is not relivant.
12501 * <script type="text/javascript">
12505 * @class Roo.data.JsonReader
12506 * @extends Roo.data.DataReader
12507 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12508 * based on mappings in a provided Roo.data.Record constructor.
12510 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12511 * in the reply previously.
12516 var RecordDef = Roo.data.Record.create([
12517 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12518 {name: 'occupation'} // This field will use "occupation" as the mapping.
12520 var myReader = new Roo.data.JsonReader({
12521 totalProperty: "results", // The property which contains the total dataset size (optional)
12522 root: "rows", // The property which contains an Array of row objects
12523 id: "id" // The property within each row object that provides an ID for the record (optional)
12527 * This would consume a JSON file like this:
12529 { 'results': 2, 'rows': [
12530 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12531 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12534 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12535 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12536 * paged from the remote server.
12537 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12538 * @cfg {String} root name of the property which contains the Array of row objects.
12539 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12540 * @cfg {Array} fields Array of field definition objects
12542 * Create a new JsonReader
12543 * @param {Object} meta Metadata configuration options
12544 * @param {Object} recordType Either an Array of field definition objects,
12545 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12547 Roo.data.JsonReader = function(meta, recordType){
12550 // set some defaults:
12551 Roo.applyIf(meta, {
12552 totalProperty: 'total',
12553 successProperty : 'success',
12558 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12560 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12563 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12564 * Used by Store query builder to append _requestMeta to params.
12567 metaFromRemote : false,
12569 * This method is only used by a DataProxy which has retrieved data from a remote server.
12570 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12571 * @return {Object} data A data block which is used by an Roo.data.Store object as
12572 * a cache of Roo.data.Records.
12574 read : function(response){
12575 var json = response.responseText;
12577 var o = /* eval:var:o */ eval("("+json+")");
12579 throw {message: "JsonReader.read: Json object not found"};
12585 this.metaFromRemote = true;
12586 this.meta = o.metaData;
12587 this.recordType = Roo.data.Record.create(o.metaData.fields);
12588 this.onMetaChange(this.meta, this.recordType, o);
12590 return this.readRecords(o);
12593 // private function a store will implement
12594 onMetaChange : function(meta, recordType, o){
12601 simpleAccess: function(obj, subsc) {
12608 getJsonAccessor: function(){
12610 return function(expr) {
12612 return(re.test(expr))
12613 ? new Function("obj", "return obj." + expr)
12618 return Roo.emptyFn;
12623 * Create a data block containing Roo.data.Records from an XML document.
12624 * @param {Object} o An object which contains an Array of row objects in the property specified
12625 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12626 * which contains the total size of the dataset.
12627 * @return {Object} data A data block which is used by an Roo.data.Store object as
12628 * a cache of Roo.data.Records.
12630 readRecords : function(o){
12632 * After any data loads, the raw JSON data is available for further custom processing.
12636 var s = this.meta, Record = this.recordType,
12637 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12639 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12641 if(s.totalProperty) {
12642 this.getTotal = this.getJsonAccessor(s.totalProperty);
12644 if(s.successProperty) {
12645 this.getSuccess = this.getJsonAccessor(s.successProperty);
12647 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12649 var g = this.getJsonAccessor(s.id);
12650 this.getId = function(rec) {
12652 return (r === undefined || r === "") ? null : r;
12655 this.getId = function(){return null;};
12658 for(var jj = 0; jj < fl; jj++){
12660 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12661 this.ef[jj] = this.getJsonAccessor(map);
12665 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12666 if(s.totalProperty){
12667 var vt = parseInt(this.getTotal(o), 10);
12672 if(s.successProperty){
12673 var vs = this.getSuccess(o);
12674 if(vs === false || vs === 'false'){
12679 for(var i = 0; i < c; i++){
12682 var id = this.getId(n);
12683 for(var j = 0; j < fl; j++){
12685 var v = this.ef[j](n);
12687 Roo.log('missing convert for ' + f.name);
12691 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12693 var record = new Record(values, id);
12695 records[i] = record;
12701 totalRecords : totalRecords
12706 * Ext JS Library 1.1.1
12707 * Copyright(c) 2006-2007, Ext JS, LLC.
12709 * Originally Released Under LGPL - original licence link has changed is not relivant.
12712 * <script type="text/javascript">
12716 * @class Roo.data.ArrayReader
12717 * @extends Roo.data.DataReader
12718 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12719 * Each element of that Array represents a row of data fields. The
12720 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12721 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12725 var RecordDef = Roo.data.Record.create([
12726 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12727 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12729 var myReader = new Roo.data.ArrayReader({
12730 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12734 * This would consume an Array like this:
12736 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12738 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12740 * Create a new JsonReader
12741 * @param {Object} meta Metadata configuration options.
12742 * @param {Object} recordType Either an Array of field definition objects
12743 * as specified to {@link Roo.data.Record#create},
12744 * or an {@link Roo.data.Record} object
12745 * created using {@link Roo.data.Record#create}.
12747 Roo.data.ArrayReader = function(meta, recordType){
12748 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12751 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12753 * Create a data block containing Roo.data.Records from an XML document.
12754 * @param {Object} o An Array of row objects which represents the dataset.
12755 * @return {Object} data A data block which is used by an Roo.data.Store object as
12756 * a cache of Roo.data.Records.
12758 readRecords : function(o){
12759 var sid = this.meta ? this.meta.id : null;
12760 var recordType = this.recordType, fields = recordType.prototype.fields;
12763 for(var i = 0; i < root.length; i++){
12766 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12767 for(var j = 0, jlen = fields.length; j < jlen; j++){
12768 var f = fields.items[j];
12769 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12770 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12772 values[f.name] = v;
12774 var record = new recordType(values, id);
12776 records[records.length] = record;
12780 totalRecords : records.length
12789 * @class Roo.bootstrap.ComboBox
12790 * @extends Roo.bootstrap.TriggerField
12791 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12792 * @cfg {Boolean} append (true|false) default false
12793 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12794 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12795 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12796 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12797 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12798 * @cfg {Boolean} animate default true
12799 * @cfg {Boolean} emptyResultText only for touch device
12800 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12801 * @cfg {String} emptyTitle default ''
12803 * Create a new ComboBox.
12804 * @param {Object} config Configuration options
12806 Roo.bootstrap.ComboBox = function(config){
12807 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12811 * Fires when the dropdown list is expanded
12812 * @param {Roo.bootstrap.ComboBox} combo This combo box
12817 * Fires when the dropdown list is collapsed
12818 * @param {Roo.bootstrap.ComboBox} combo This combo box
12822 * @event beforeselect
12823 * Fires before a list item is selected. Return false to cancel the selection.
12824 * @param {Roo.bootstrap.ComboBox} combo This combo box
12825 * @param {Roo.data.Record} record The data record returned from the underlying store
12826 * @param {Number} index The index of the selected item in the dropdown list
12828 'beforeselect' : true,
12831 * Fires when a list item is selected
12832 * @param {Roo.bootstrap.ComboBox} combo This combo box
12833 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12834 * @param {Number} index The index of the selected item in the dropdown list
12838 * @event beforequery
12839 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12840 * The event object passed has these properties:
12841 * @param {Roo.bootstrap.ComboBox} combo This combo box
12842 * @param {String} query The query
12843 * @param {Boolean} forceAll true to force "all" query
12844 * @param {Boolean} cancel true to cancel the query
12845 * @param {Object} e The query event object
12847 'beforequery': true,
12850 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12851 * @param {Roo.bootstrap.ComboBox} combo This combo box
12856 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12857 * @param {Roo.bootstrap.ComboBox} combo This combo box
12858 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12863 * Fires when the remove value from the combobox array
12864 * @param {Roo.bootstrap.ComboBox} combo This combo box
12868 * @event afterremove
12869 * Fires when the remove value from the combobox array
12870 * @param {Roo.bootstrap.ComboBox} combo This combo box
12872 'afterremove' : true,
12874 * @event specialfilter
12875 * Fires when specialfilter
12876 * @param {Roo.bootstrap.ComboBox} combo This combo box
12878 'specialfilter' : true,
12881 * Fires when tick the element
12882 * @param {Roo.bootstrap.ComboBox} combo This combo box
12886 * @event touchviewdisplay
12887 * Fires when touch view require special display (default is using displayField)
12888 * @param {Roo.bootstrap.ComboBox} combo This combo box
12889 * @param {Object} cfg set html .
12891 'touchviewdisplay' : true
12896 this.tickItems = [];
12898 this.selectedIndex = -1;
12899 if(this.mode == 'local'){
12900 if(config.queryDelay === undefined){
12901 this.queryDelay = 10;
12903 if(config.minChars === undefined){
12909 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12912 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12913 * rendering into an Roo.Editor, defaults to false)
12916 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12917 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12920 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12923 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12924 * the dropdown list (defaults to undefined, with no header element)
12928 * @cfg {String/Roo.Template} tpl The template to use to render the output
12932 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12934 listWidth: undefined,
12936 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12937 * mode = 'remote' or 'text' if mode = 'local')
12939 displayField: undefined,
12942 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12943 * mode = 'remote' or 'value' if mode = 'local').
12944 * Note: use of a valueField requires the user make a selection
12945 * in order for a value to be mapped.
12947 valueField: undefined,
12949 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12954 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12955 * field's data value (defaults to the underlying DOM element's name)
12957 hiddenName: undefined,
12959 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12963 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12965 selectedClass: 'active',
12968 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12972 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12973 * anchor positions (defaults to 'tl-bl')
12975 listAlign: 'tl-bl?',
12977 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12981 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12982 * query specified by the allQuery config option (defaults to 'query')
12984 triggerAction: 'query',
12986 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12987 * (defaults to 4, does not apply if editable = false)
12991 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12992 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12996 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12997 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13001 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13002 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13006 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13007 * when editable = true (defaults to false)
13009 selectOnFocus:false,
13011 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13013 queryParam: 'query',
13015 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13016 * when mode = 'remote' (defaults to 'Loading...')
13018 loadingText: 'Loading...',
13020 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13024 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13028 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13029 * traditional select (defaults to true)
13033 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13037 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13041 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13042 * listWidth has a higher value)
13046 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13047 * allow the user to set arbitrary text into the field (defaults to false)
13049 forceSelection:false,
13051 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13052 * if typeAhead = true (defaults to 250)
13054 typeAheadDelay : 250,
13056 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13057 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13059 valueNotFoundText : undefined,
13061 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13063 blockFocus : false,
13066 * @cfg {Boolean} disableClear Disable showing of clear button.
13068 disableClear : false,
13070 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13072 alwaysQuery : false,
13075 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13080 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13082 invalidClass : "has-warning",
13085 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13087 validClass : "has-success",
13090 * @cfg {Boolean} specialFilter (true|false) special filter default false
13092 specialFilter : false,
13095 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13097 mobileTouchView : true,
13100 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13102 useNativeIOS : false,
13105 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13107 mobile_restrict_height : false,
13109 ios_options : false,
13121 btnPosition : 'right',
13122 triggerList : true,
13123 showToggleBtn : true,
13125 emptyResultText: 'Empty',
13126 triggerText : 'Select',
13129 // element that contains real text value.. (when hidden is used..)
13131 getAutoCreate : function()
13136 * Render classic select for iso
13139 if(Roo.isIOS && this.useNativeIOS){
13140 cfg = this.getAutoCreateNativeIOS();
13148 if(Roo.isTouch && this.mobileTouchView){
13149 cfg = this.getAutoCreateTouchView();
13156 if(!this.tickable){
13157 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13162 * ComboBox with tickable selections
13165 var align = this.labelAlign || this.parentLabelAlign();
13168 cls : 'form-group roo-combobox-tickable' //input-group
13171 var btn_text_select = '';
13172 var btn_text_done = '';
13173 var btn_text_cancel = '';
13175 if (this.btn_text_show) {
13176 btn_text_select = 'Select';
13177 btn_text_done = 'Done';
13178 btn_text_cancel = 'Cancel';
13183 cls : 'tickable-buttons',
13188 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13189 //html : this.triggerText
13190 html: btn_text_select
13196 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13198 html: btn_text_done
13204 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13206 html: btn_text_cancel
13212 buttons.cn.unshift({
13214 cls: 'roo-select2-search-field-input'
13220 Roo.each(buttons.cn, function(c){
13222 c.cls += ' btn-' + _this.size;
13225 if (_this.disabled) {
13236 cls: 'form-hidden-field'
13240 cls: 'roo-select2-choices',
13244 cls: 'roo-select2-search-field',
13255 cls: 'roo-select2-container input-group roo-select2-container-multi',
13260 // cls: 'typeahead typeahead-long dropdown-menu',
13261 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13266 if(this.hasFeedback && !this.allowBlank){
13270 cls: 'glyphicon form-control-feedback'
13273 combobox.cn.push(feedback);
13277 if (align ==='left' && this.fieldLabel.length) {
13279 cfg.cls += ' roo-form-group-label-left';
13284 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13285 tooltip : 'This field is required'
13290 cls : 'control-label',
13291 html : this.fieldLabel
13303 var labelCfg = cfg.cn[1];
13304 var contentCfg = cfg.cn[2];
13307 if(this.indicatorpos == 'right'){
13313 cls : 'control-label',
13317 html : this.fieldLabel
13321 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13322 tooltip : 'This field is required'
13337 labelCfg = cfg.cn[0];
13338 contentCfg = cfg.cn[1];
13342 if(this.labelWidth > 12){
13343 labelCfg.style = "width: " + this.labelWidth + 'px';
13346 if(this.labelWidth < 13 && this.labelmd == 0){
13347 this.labelmd = this.labelWidth;
13350 if(this.labellg > 0){
13351 labelCfg.cls += ' col-lg-' + this.labellg;
13352 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13355 if(this.labelmd > 0){
13356 labelCfg.cls += ' col-md-' + this.labelmd;
13357 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13360 if(this.labelsm > 0){
13361 labelCfg.cls += ' col-sm-' + this.labelsm;
13362 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13365 if(this.labelxs > 0){
13366 labelCfg.cls += ' col-xs-' + this.labelxs;
13367 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13371 } else if ( this.fieldLabel.length) {
13372 // Roo.log(" label");
13376 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13377 tooltip : 'This field is required'
13381 //cls : 'input-group-addon',
13382 html : this.fieldLabel
13387 if(this.indicatorpos == 'right'){
13391 //cls : 'input-group-addon',
13392 html : this.fieldLabel
13396 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13397 tooltip : 'This field is required'
13406 // Roo.log(" no label && no align");
13413 ['xs','sm','md','lg'].map(function(size){
13414 if (settings[size]) {
13415 cfg.cls += ' col-' + size + '-' + settings[size];
13423 _initEventsCalled : false,
13426 initEvents: function()
13428 if (this._initEventsCalled) { // as we call render... prevent looping...
13431 this._initEventsCalled = true;
13434 throw "can not find store for combo";
13437 this.indicator = this.indicatorEl();
13439 this.store = Roo.factory(this.store, Roo.data);
13440 this.store.parent = this;
13442 // if we are building from html. then this element is so complex, that we can not really
13443 // use the rendered HTML.
13444 // so we have to trash and replace the previous code.
13445 if (Roo.XComponent.build_from_html) {
13446 // remove this element....
13447 var e = this.el.dom, k=0;
13448 while (e ) { e = e.previousSibling; ++k;}
13453 this.rendered = false;
13455 this.render(this.parent().getChildContainer(true), k);
13458 if(Roo.isIOS && this.useNativeIOS){
13459 this.initIOSView();
13467 if(Roo.isTouch && this.mobileTouchView){
13468 this.initTouchView();
13473 this.initTickableEvents();
13477 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13479 if(this.hiddenName){
13481 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13483 this.hiddenField.dom.value =
13484 this.hiddenValue !== undefined ? this.hiddenValue :
13485 this.value !== undefined ? this.value : '';
13487 // prevent input submission
13488 this.el.dom.removeAttribute('name');
13489 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13494 // this.el.dom.setAttribute('autocomplete', 'off');
13497 var cls = 'x-combo-list';
13499 //this.list = new Roo.Layer({
13500 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13506 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13507 _this.list.setWidth(lw);
13510 this.list.on('mouseover', this.onViewOver, this);
13511 this.list.on('mousemove', this.onViewMove, this);
13512 this.list.on('scroll', this.onViewScroll, this);
13515 this.list.swallowEvent('mousewheel');
13516 this.assetHeight = 0;
13519 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13520 this.assetHeight += this.header.getHeight();
13523 this.innerList = this.list.createChild({cls:cls+'-inner'});
13524 this.innerList.on('mouseover', this.onViewOver, this);
13525 this.innerList.on('mousemove', this.onViewMove, this);
13526 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13528 if(this.allowBlank && !this.pageSize && !this.disableClear){
13529 this.footer = this.list.createChild({cls:cls+'-ft'});
13530 this.pageTb = new Roo.Toolbar(this.footer);
13534 this.footer = this.list.createChild({cls:cls+'-ft'});
13535 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13536 {pageSize: this.pageSize});
13540 if (this.pageTb && this.allowBlank && !this.disableClear) {
13542 this.pageTb.add(new Roo.Toolbar.Fill(), {
13543 cls: 'x-btn-icon x-btn-clear',
13545 handler: function()
13548 _this.clearValue();
13549 _this.onSelect(false, -1);
13554 this.assetHeight += this.footer.getHeight();
13559 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13562 this.view = new Roo.View(this.list, this.tpl, {
13563 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13565 //this.view.wrapEl.setDisplayed(false);
13566 this.view.on('click', this.onViewClick, this);
13569 this.store.on('beforeload', this.onBeforeLoad, this);
13570 this.store.on('load', this.onLoad, this);
13571 this.store.on('loadexception', this.onLoadException, this);
13573 if(this.resizable){
13574 this.resizer = new Roo.Resizable(this.list, {
13575 pinned:true, handles:'se'
13577 this.resizer.on('resize', function(r, w, h){
13578 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13579 this.listWidth = w;
13580 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13581 this.restrictHeight();
13583 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13586 if(!this.editable){
13587 this.editable = true;
13588 this.setEditable(false);
13593 if (typeof(this.events.add.listeners) != 'undefined') {
13595 this.addicon = this.wrap.createChild(
13596 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13598 this.addicon.on('click', function(e) {
13599 this.fireEvent('add', this);
13602 if (typeof(this.events.edit.listeners) != 'undefined') {
13604 this.editicon = this.wrap.createChild(
13605 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13606 if (this.addicon) {
13607 this.editicon.setStyle('margin-left', '40px');
13609 this.editicon.on('click', function(e) {
13611 // we fire even if inothing is selected..
13612 this.fireEvent('edit', this, this.lastData );
13618 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13619 "up" : function(e){
13620 this.inKeyMode = true;
13624 "down" : function(e){
13625 if(!this.isExpanded()){
13626 this.onTriggerClick();
13628 this.inKeyMode = true;
13633 "enter" : function(e){
13634 // this.onViewClick();
13638 if(this.fireEvent("specialkey", this, e)){
13639 this.onViewClick(false);
13645 "esc" : function(e){
13649 "tab" : function(e){
13652 if(this.fireEvent("specialkey", this, e)){
13653 this.onViewClick(false);
13661 doRelay : function(foo, bar, hname){
13662 if(hname == 'down' || this.scope.isExpanded()){
13663 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13672 this.queryDelay = Math.max(this.queryDelay || 10,
13673 this.mode == 'local' ? 10 : 250);
13676 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13678 if(this.typeAhead){
13679 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13681 if(this.editable !== false){
13682 this.inputEl().on("keyup", this.onKeyUp, this);
13684 if(this.forceSelection){
13685 this.inputEl().on('blur', this.doForce, this);
13689 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13690 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13694 initTickableEvents: function()
13698 if(this.hiddenName){
13700 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13702 this.hiddenField.dom.value =
13703 this.hiddenValue !== undefined ? this.hiddenValue :
13704 this.value !== undefined ? this.value : '';
13706 // prevent input submission
13707 this.el.dom.removeAttribute('name');
13708 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13713 // this.list = this.el.select('ul.dropdown-menu',true).first();
13715 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13716 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13717 if(this.triggerList){
13718 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13721 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13722 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13724 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13725 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13727 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13728 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13730 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13731 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13732 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13735 this.cancelBtn.hide();
13740 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13741 _this.list.setWidth(lw);
13744 this.list.on('mouseover', this.onViewOver, this);
13745 this.list.on('mousemove', this.onViewMove, this);
13747 this.list.on('scroll', this.onViewScroll, this);
13750 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13751 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13754 this.view = new Roo.View(this.list, this.tpl, {
13759 selectedClass: this.selectedClass
13762 //this.view.wrapEl.setDisplayed(false);
13763 this.view.on('click', this.onViewClick, this);
13767 this.store.on('beforeload', this.onBeforeLoad, this);
13768 this.store.on('load', this.onLoad, this);
13769 this.store.on('loadexception', this.onLoadException, this);
13772 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13773 "up" : function(e){
13774 this.inKeyMode = true;
13778 "down" : function(e){
13779 this.inKeyMode = true;
13783 "enter" : function(e){
13784 if(this.fireEvent("specialkey", this, e)){
13785 this.onViewClick(false);
13791 "esc" : function(e){
13792 this.onTickableFooterButtonClick(e, false, false);
13795 "tab" : function(e){
13796 this.fireEvent("specialkey", this, e);
13798 this.onTickableFooterButtonClick(e, false, false);
13805 doRelay : function(e, fn, key){
13806 if(this.scope.isExpanded()){
13807 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13816 this.queryDelay = Math.max(this.queryDelay || 10,
13817 this.mode == 'local' ? 10 : 250);
13820 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13822 if(this.typeAhead){
13823 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13826 if(this.editable !== false){
13827 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13830 this.indicator = this.indicatorEl();
13832 if(this.indicator){
13833 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13834 this.indicator.hide();
13839 onDestroy : function(){
13841 this.view.setStore(null);
13842 this.view.el.removeAllListeners();
13843 this.view.el.remove();
13844 this.view.purgeListeners();
13847 this.list.dom.innerHTML = '';
13851 this.store.un('beforeload', this.onBeforeLoad, this);
13852 this.store.un('load', this.onLoad, this);
13853 this.store.un('loadexception', this.onLoadException, this);
13855 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13859 fireKey : function(e){
13860 if(e.isNavKeyPress() && !this.list.isVisible()){
13861 this.fireEvent("specialkey", this, e);
13866 onResize: function(w, h){
13867 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13869 // if(typeof w != 'number'){
13870 // // we do not handle it!?!?
13873 // var tw = this.trigger.getWidth();
13874 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13875 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13877 // this.inputEl().setWidth( this.adjustWidth('input', x));
13879 // //this.trigger.setStyle('left', x+'px');
13881 // if(this.list && this.listWidth === undefined){
13882 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13883 // this.list.setWidth(lw);
13884 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13892 * Allow or prevent the user from directly editing the field text. If false is passed,
13893 * the user will only be able to select from the items defined in the dropdown list. This method
13894 * is the runtime equivalent of setting the 'editable' config option at config time.
13895 * @param {Boolean} value True to allow the user to directly edit the field text
13897 setEditable : function(value){
13898 if(value == this.editable){
13901 this.editable = value;
13903 this.inputEl().dom.setAttribute('readOnly', true);
13904 this.inputEl().on('mousedown', this.onTriggerClick, this);
13905 this.inputEl().addClass('x-combo-noedit');
13907 this.inputEl().dom.setAttribute('readOnly', false);
13908 this.inputEl().un('mousedown', this.onTriggerClick, this);
13909 this.inputEl().removeClass('x-combo-noedit');
13915 onBeforeLoad : function(combo,opts){
13916 if(!this.hasFocus){
13920 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13922 this.restrictHeight();
13923 this.selectedIndex = -1;
13927 onLoad : function(){
13929 this.hasQuery = false;
13931 if(!this.hasFocus){
13935 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13936 this.loading.hide();
13939 if(this.store.getCount() > 0){
13942 this.restrictHeight();
13943 if(this.lastQuery == this.allQuery){
13944 if(this.editable && !this.tickable){
13945 this.inputEl().dom.select();
13949 !this.selectByValue(this.value, true) &&
13952 !this.store.lastOptions ||
13953 typeof(this.store.lastOptions.add) == 'undefined' ||
13954 this.store.lastOptions.add != true
13957 this.select(0, true);
13960 if(this.autoFocus){
13963 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13964 this.taTask.delay(this.typeAheadDelay);
13968 this.onEmptyResults();
13974 onLoadException : function()
13976 this.hasQuery = false;
13978 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13979 this.loading.hide();
13982 if(this.tickable && this.editable){
13987 // only causes errors at present
13988 //Roo.log(this.store.reader.jsonData);
13989 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13991 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13997 onTypeAhead : function(){
13998 if(this.store.getCount() > 0){
13999 var r = this.store.getAt(0);
14000 var newValue = r.data[this.displayField];
14001 var len = newValue.length;
14002 var selStart = this.getRawValue().length;
14004 if(selStart != len){
14005 this.setRawValue(newValue);
14006 this.selectText(selStart, newValue.length);
14012 onSelect : function(record, index){
14014 if(this.fireEvent('beforeselect', this, record, index) !== false){
14016 this.setFromData(index > -1 ? record.data : false);
14019 this.fireEvent('select', this, record, index);
14024 * Returns the currently selected field value or empty string if no value is set.
14025 * @return {String} value The selected value
14027 getValue : function()
14029 if(Roo.isIOS && this.useNativeIOS){
14030 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14034 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14037 if(this.valueField){
14038 return typeof this.value != 'undefined' ? this.value : '';
14040 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14044 getRawValue : function()
14046 if(Roo.isIOS && this.useNativeIOS){
14047 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14050 var v = this.inputEl().getValue();
14056 * Clears any text/value currently set in the field
14058 clearValue : function(){
14060 if(this.hiddenField){
14061 this.hiddenField.dom.value = '';
14064 this.setRawValue('');
14065 this.lastSelectionText = '';
14066 this.lastData = false;
14068 var close = this.closeTriggerEl();
14079 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14080 * will be displayed in the field. If the value does not match the data value of an existing item,
14081 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14082 * Otherwise the field will be blank (although the value will still be set).
14083 * @param {String} value The value to match
14085 setValue : function(v)
14087 if(Roo.isIOS && this.useNativeIOS){
14088 this.setIOSValue(v);
14098 if(this.valueField){
14099 var r = this.findRecord(this.valueField, v);
14101 text = r.data[this.displayField];
14102 }else if(this.valueNotFoundText !== undefined){
14103 text = this.valueNotFoundText;
14106 this.lastSelectionText = text;
14107 if(this.hiddenField){
14108 this.hiddenField.dom.value = v;
14110 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14113 var close = this.closeTriggerEl();
14116 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14122 * @property {Object} the last set data for the element
14127 * Sets the value of the field based on a object which is related to the record format for the store.
14128 * @param {Object} value the value to set as. or false on reset?
14130 setFromData : function(o){
14137 var dv = ''; // display value
14138 var vv = ''; // value value..
14140 if (this.displayField) {
14141 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14143 // this is an error condition!!!
14144 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14147 if(this.valueField){
14148 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14151 var close = this.closeTriggerEl();
14154 if(dv.length || vv * 1 > 0){
14156 this.blockFocus=true;
14162 if(this.hiddenField){
14163 this.hiddenField.dom.value = vv;
14165 this.lastSelectionText = dv;
14166 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14170 // no hidden field.. - we store the value in 'value', but still display
14171 // display field!!!!
14172 this.lastSelectionText = dv;
14173 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14180 reset : function(){
14181 // overridden so that last data is reset..
14188 this.setValue(this.originalValue);
14189 //this.clearInvalid();
14190 this.lastData = false;
14192 this.view.clearSelections();
14198 findRecord : function(prop, value){
14200 if(this.store.getCount() > 0){
14201 this.store.each(function(r){
14202 if(r.data[prop] == value){
14212 getName: function()
14214 // returns hidden if it's set..
14215 if (!this.rendered) {return ''};
14216 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14220 onViewMove : function(e, t){
14221 this.inKeyMode = false;
14225 onViewOver : function(e, t){
14226 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14229 var item = this.view.findItemFromChild(t);
14232 var index = this.view.indexOf(item);
14233 this.select(index, false);
14238 onViewClick : function(view, doFocus, el, e)
14240 var index = this.view.getSelectedIndexes()[0];
14242 var r = this.store.getAt(index);
14246 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14253 Roo.each(this.tickItems, function(v,k){
14255 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14257 _this.tickItems.splice(k, 1);
14259 if(typeof(e) == 'undefined' && view == false){
14260 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14272 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14273 this.tickItems.push(r.data);
14276 if(typeof(e) == 'undefined' && view == false){
14277 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14284 this.onSelect(r, index);
14286 if(doFocus !== false && !this.blockFocus){
14287 this.inputEl().focus();
14292 restrictHeight : function(){
14293 //this.innerList.dom.style.height = '';
14294 //var inner = this.innerList.dom;
14295 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14296 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14297 //this.list.beginUpdate();
14298 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14299 this.list.alignTo(this.inputEl(), this.listAlign);
14300 this.list.alignTo(this.inputEl(), this.listAlign);
14301 //this.list.endUpdate();
14305 onEmptyResults : function(){
14307 if(this.tickable && this.editable){
14308 this.hasFocus = false;
14309 this.restrictHeight();
14317 * Returns true if the dropdown list is expanded, else false.
14319 isExpanded : function(){
14320 return this.list.isVisible();
14324 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14325 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14326 * @param {String} value The data value of the item to select
14327 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14328 * selected item if it is not currently in view (defaults to true)
14329 * @return {Boolean} True if the value matched an item in the list, else false
14331 selectByValue : function(v, scrollIntoView){
14332 if(v !== undefined && v !== null){
14333 var r = this.findRecord(this.valueField || this.displayField, v);
14335 this.select(this.store.indexOf(r), scrollIntoView);
14343 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14344 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14345 * @param {Number} index The zero-based index of the list item to select
14346 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14347 * selected item if it is not currently in view (defaults to true)
14349 select : function(index, scrollIntoView){
14350 this.selectedIndex = index;
14351 this.view.select(index);
14352 if(scrollIntoView !== false){
14353 var el = this.view.getNode(index);
14355 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14358 this.list.scrollChildIntoView(el, false);
14364 selectNext : function(){
14365 var ct = this.store.getCount();
14367 if(this.selectedIndex == -1){
14369 }else if(this.selectedIndex < ct-1){
14370 this.select(this.selectedIndex+1);
14376 selectPrev : function(){
14377 var ct = this.store.getCount();
14379 if(this.selectedIndex == -1){
14381 }else if(this.selectedIndex != 0){
14382 this.select(this.selectedIndex-1);
14388 onKeyUp : function(e){
14389 if(this.editable !== false && !e.isSpecialKey()){
14390 this.lastKey = e.getKey();
14391 this.dqTask.delay(this.queryDelay);
14396 validateBlur : function(){
14397 return !this.list || !this.list.isVisible();
14401 initQuery : function(){
14403 var v = this.getRawValue();
14405 if(this.tickable && this.editable){
14406 v = this.tickableInputEl().getValue();
14413 doForce : function(){
14414 if(this.inputEl().dom.value.length > 0){
14415 this.inputEl().dom.value =
14416 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14422 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14423 * query allowing the query action to be canceled if needed.
14424 * @param {String} query The SQL query to execute
14425 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14426 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14427 * saved in the current store (defaults to false)
14429 doQuery : function(q, forceAll){
14431 if(q === undefined || q === null){
14436 forceAll: forceAll,
14440 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14445 forceAll = qe.forceAll;
14446 if(forceAll === true || (q.length >= this.minChars)){
14448 this.hasQuery = true;
14450 if(this.lastQuery != q || this.alwaysQuery){
14451 this.lastQuery = q;
14452 if(this.mode == 'local'){
14453 this.selectedIndex = -1;
14455 this.store.clearFilter();
14458 if(this.specialFilter){
14459 this.fireEvent('specialfilter', this);
14464 this.store.filter(this.displayField, q);
14467 this.store.fireEvent("datachanged", this.store);
14474 this.store.baseParams[this.queryParam] = q;
14476 var options = {params : this.getParams(q)};
14479 options.add = true;
14480 options.params.start = this.page * this.pageSize;
14483 this.store.load(options);
14486 * this code will make the page width larger, at the beginning, the list not align correctly,
14487 * we should expand the list on onLoad
14488 * so command out it
14493 this.selectedIndex = -1;
14498 this.loadNext = false;
14502 getParams : function(q){
14504 //p[this.queryParam] = q;
14508 p.limit = this.pageSize;
14514 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14516 collapse : function(){
14517 if(!this.isExpanded()){
14523 this.hasFocus = false;
14527 this.cancelBtn.hide();
14528 this.trigger.show();
14531 this.tickableInputEl().dom.value = '';
14532 this.tickableInputEl().blur();
14537 Roo.get(document).un('mousedown', this.collapseIf, this);
14538 Roo.get(document).un('mousewheel', this.collapseIf, this);
14539 if (!this.editable) {
14540 Roo.get(document).un('keydown', this.listKeyPress, this);
14542 this.fireEvent('collapse', this);
14548 collapseIf : function(e){
14549 var in_combo = e.within(this.el);
14550 var in_list = e.within(this.list);
14551 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14553 if (in_combo || in_list || is_list) {
14554 //e.stopPropagation();
14559 this.onTickableFooterButtonClick(e, false, false);
14567 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14569 expand : function(){
14571 if(this.isExpanded() || !this.hasFocus){
14575 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14576 this.list.setWidth(lw);
14582 this.restrictHeight();
14586 this.tickItems = Roo.apply([], this.item);
14589 this.cancelBtn.show();
14590 this.trigger.hide();
14593 this.tickableInputEl().focus();
14598 Roo.get(document).on('mousedown', this.collapseIf, this);
14599 Roo.get(document).on('mousewheel', this.collapseIf, this);
14600 if (!this.editable) {
14601 Roo.get(document).on('keydown', this.listKeyPress, this);
14604 this.fireEvent('expand', this);
14608 // Implements the default empty TriggerField.onTriggerClick function
14609 onTriggerClick : function(e)
14611 Roo.log('trigger click');
14613 if(this.disabled || !this.triggerList){
14618 this.loadNext = false;
14620 if(this.isExpanded()){
14622 if (!this.blockFocus) {
14623 this.inputEl().focus();
14627 this.hasFocus = true;
14628 if(this.triggerAction == 'all') {
14629 this.doQuery(this.allQuery, true);
14631 this.doQuery(this.getRawValue());
14633 if (!this.blockFocus) {
14634 this.inputEl().focus();
14639 onTickableTriggerClick : function(e)
14646 this.loadNext = false;
14647 this.hasFocus = true;
14649 if(this.triggerAction == 'all') {
14650 this.doQuery(this.allQuery, true);
14652 this.doQuery(this.getRawValue());
14656 onSearchFieldClick : function(e)
14658 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14659 this.onTickableFooterButtonClick(e, false, false);
14663 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14668 this.loadNext = false;
14669 this.hasFocus = true;
14671 if(this.triggerAction == 'all') {
14672 this.doQuery(this.allQuery, true);
14674 this.doQuery(this.getRawValue());
14678 listKeyPress : function(e)
14680 //Roo.log('listkeypress');
14681 // scroll to first matching element based on key pres..
14682 if (e.isSpecialKey()) {
14685 var k = String.fromCharCode(e.getKey()).toUpperCase();
14688 var csel = this.view.getSelectedNodes();
14689 var cselitem = false;
14691 var ix = this.view.indexOf(csel[0]);
14692 cselitem = this.store.getAt(ix);
14693 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14699 this.store.each(function(v) {
14701 // start at existing selection.
14702 if (cselitem.id == v.id) {
14708 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14709 match = this.store.indexOf(v);
14715 if (match === false) {
14716 return true; // no more action?
14719 this.view.select(match);
14720 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14721 sn.scrollIntoView(sn.dom.parentNode, false);
14724 onViewScroll : function(e, t){
14726 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){
14730 this.hasQuery = true;
14732 this.loading = this.list.select('.loading', true).first();
14734 if(this.loading === null){
14735 this.list.createChild({
14737 cls: 'loading roo-select2-more-results roo-select2-active',
14738 html: 'Loading more results...'
14741 this.loading = this.list.select('.loading', true).first();
14743 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14745 this.loading.hide();
14748 this.loading.show();
14753 this.loadNext = true;
14755 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14760 addItem : function(o)
14762 var dv = ''; // display value
14764 if (this.displayField) {
14765 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14767 // this is an error condition!!!
14768 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14775 var choice = this.choices.createChild({
14777 cls: 'roo-select2-search-choice',
14786 cls: 'roo-select2-search-choice-close fa fa-times',
14791 }, this.searchField);
14793 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14795 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14803 this.inputEl().dom.value = '';
14808 onRemoveItem : function(e, _self, o)
14810 e.preventDefault();
14812 this.lastItem = Roo.apply([], this.item);
14814 var index = this.item.indexOf(o.data) * 1;
14817 Roo.log('not this item?!');
14821 this.item.splice(index, 1);
14826 this.fireEvent('remove', this, e);
14832 syncValue : function()
14834 if(!this.item.length){
14841 Roo.each(this.item, function(i){
14842 if(_this.valueField){
14843 value.push(i[_this.valueField]);
14850 this.value = value.join(',');
14852 if(this.hiddenField){
14853 this.hiddenField.dom.value = this.value;
14856 this.store.fireEvent("datachanged", this.store);
14861 clearItem : function()
14863 if(!this.multiple){
14869 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14877 if(this.tickable && !Roo.isTouch){
14878 this.view.refresh();
14882 inputEl: function ()
14884 if(Roo.isIOS && this.useNativeIOS){
14885 return this.el.select('select.roo-ios-select', true).first();
14888 if(Roo.isTouch && this.mobileTouchView){
14889 return this.el.select('input.form-control',true).first();
14893 return this.searchField;
14896 return this.el.select('input.form-control',true).first();
14899 onTickableFooterButtonClick : function(e, btn, el)
14901 e.preventDefault();
14903 this.lastItem = Roo.apply([], this.item);
14905 if(btn && btn.name == 'cancel'){
14906 this.tickItems = Roo.apply([], this.item);
14915 Roo.each(this.tickItems, function(o){
14923 validate : function()
14925 if(this.getVisibilityEl().hasClass('hidden')){
14929 var v = this.getRawValue();
14932 v = this.getValue();
14935 if(this.disabled || this.allowBlank || v.length){
14940 this.markInvalid();
14944 tickableInputEl : function()
14946 if(!this.tickable || !this.editable){
14947 return this.inputEl();
14950 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14954 getAutoCreateTouchView : function()
14959 cls: 'form-group' //input-group
14965 type : this.inputType,
14966 cls : 'form-control x-combo-noedit',
14967 autocomplete: 'new-password',
14968 placeholder : this.placeholder || '',
14973 input.name = this.name;
14977 input.cls += ' input-' + this.size;
14980 if (this.disabled) {
14981 input.disabled = true;
14992 inputblock.cls += ' input-group';
14994 inputblock.cn.unshift({
14996 cls : 'input-group-addon',
15001 if(this.removable && !this.multiple){
15002 inputblock.cls += ' roo-removable';
15004 inputblock.cn.push({
15007 cls : 'roo-combo-removable-btn close'
15011 if(this.hasFeedback && !this.allowBlank){
15013 inputblock.cls += ' has-feedback';
15015 inputblock.cn.push({
15017 cls: 'glyphicon form-control-feedback'
15024 inputblock.cls += (this.before) ? '' : ' input-group';
15026 inputblock.cn.push({
15028 cls : 'input-group-addon',
15039 cls: 'form-hidden-field'
15053 cls: 'form-hidden-field'
15057 cls: 'roo-select2-choices',
15061 cls: 'roo-select2-search-field',
15074 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15080 if(!this.multiple && this.showToggleBtn){
15087 if (this.caret != false) {
15090 cls: 'fa fa-' + this.caret
15097 cls : 'input-group-addon btn dropdown-toggle',
15102 cls: 'combobox-clear',
15116 combobox.cls += ' roo-select2-container-multi';
15119 var align = this.labelAlign || this.parentLabelAlign();
15121 if (align ==='left' && this.fieldLabel.length) {
15126 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15127 tooltip : 'This field is required'
15131 cls : 'control-label',
15132 html : this.fieldLabel
15143 var labelCfg = cfg.cn[1];
15144 var contentCfg = cfg.cn[2];
15147 if(this.indicatorpos == 'right'){
15152 cls : 'control-label',
15156 html : this.fieldLabel
15160 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15161 tooltip : 'This field is required'
15174 labelCfg = cfg.cn[0];
15175 contentCfg = cfg.cn[1];
15180 if(this.labelWidth > 12){
15181 labelCfg.style = "width: " + this.labelWidth + 'px';
15184 if(this.labelWidth < 13 && this.labelmd == 0){
15185 this.labelmd = this.labelWidth;
15188 if(this.labellg > 0){
15189 labelCfg.cls += ' col-lg-' + this.labellg;
15190 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15193 if(this.labelmd > 0){
15194 labelCfg.cls += ' col-md-' + this.labelmd;
15195 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15198 if(this.labelsm > 0){
15199 labelCfg.cls += ' col-sm-' + this.labelsm;
15200 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15203 if(this.labelxs > 0){
15204 labelCfg.cls += ' col-xs-' + this.labelxs;
15205 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15209 } else if ( this.fieldLabel.length) {
15213 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15214 tooltip : 'This field is required'
15218 cls : 'control-label',
15219 html : this.fieldLabel
15230 if(this.indicatorpos == 'right'){
15234 cls : 'control-label',
15235 html : this.fieldLabel,
15239 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15240 tooltip : 'This field is required'
15257 var settings = this;
15259 ['xs','sm','md','lg'].map(function(size){
15260 if (settings[size]) {
15261 cfg.cls += ' col-' + size + '-' + settings[size];
15268 initTouchView : function()
15270 this.renderTouchView();
15272 this.touchViewEl.on('scroll', function(){
15273 this.el.dom.scrollTop = 0;
15276 this.originalValue = this.getValue();
15278 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15280 this.inputEl().on("click", this.showTouchView, this);
15281 if (this.triggerEl) {
15282 this.triggerEl.on("click", this.showTouchView, this);
15286 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15287 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15289 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15291 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15292 this.store.on('load', this.onTouchViewLoad, this);
15293 this.store.on('loadexception', this.onTouchViewLoadException, this);
15295 if(this.hiddenName){
15297 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15299 this.hiddenField.dom.value =
15300 this.hiddenValue !== undefined ? this.hiddenValue :
15301 this.value !== undefined ? this.value : '';
15303 this.el.dom.removeAttribute('name');
15304 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15308 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15309 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15312 if(this.removable && !this.multiple){
15313 var close = this.closeTriggerEl();
15315 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15316 close.on('click', this.removeBtnClick, this, close);
15320 * fix the bug in Safari iOS8
15322 this.inputEl().on("focus", function(e){
15323 document.activeElement.blur();
15326 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15333 renderTouchView : function()
15335 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15336 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15338 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15339 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15341 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15342 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15343 this.touchViewBodyEl.setStyle('overflow', 'auto');
15345 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15346 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15348 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15349 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15353 showTouchView : function()
15359 this.touchViewHeaderEl.hide();
15361 if(this.modalTitle.length){
15362 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15363 this.touchViewHeaderEl.show();
15366 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15367 this.touchViewEl.show();
15369 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15371 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15372 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15374 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15376 if(this.modalTitle.length){
15377 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15380 this.touchViewBodyEl.setHeight(bodyHeight);
15384 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15386 this.touchViewEl.addClass('in');
15389 if(this._touchViewMask){
15390 Roo.get(document.body).addClass("x-body-masked");
15391 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15392 this._touchViewMask.setStyle('z-index', 10000);
15393 this._touchViewMask.addClass('show');
15396 this.doTouchViewQuery();
15400 hideTouchView : function()
15402 this.touchViewEl.removeClass('in');
15406 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15408 this.touchViewEl.setStyle('display', 'none');
15411 if(this._touchViewMask){
15412 this._touchViewMask.removeClass('show');
15413 Roo.get(document.body).removeClass("x-body-masked");
15417 setTouchViewValue : function()
15424 Roo.each(this.tickItems, function(o){
15429 this.hideTouchView();
15432 doTouchViewQuery : function()
15441 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15445 if(!this.alwaysQuery || this.mode == 'local'){
15446 this.onTouchViewLoad();
15453 onTouchViewBeforeLoad : function(combo,opts)
15459 onTouchViewLoad : function()
15461 if(this.store.getCount() < 1){
15462 this.onTouchViewEmptyResults();
15466 this.clearTouchView();
15468 var rawValue = this.getRawValue();
15470 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15472 this.tickItems = [];
15474 this.store.data.each(function(d, rowIndex){
15475 var row = this.touchViewListGroup.createChild(template);
15477 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15478 row.addClass(d.data.cls);
15481 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15484 html : d.data[this.displayField]
15487 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15488 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15491 row.removeClass('selected');
15492 if(!this.multiple && this.valueField &&
15493 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15496 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15497 row.addClass('selected');
15500 if(this.multiple && this.valueField &&
15501 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15505 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15506 this.tickItems.push(d.data);
15509 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15513 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15515 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15517 if(this.modalTitle.length){
15518 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15521 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15523 if(this.mobile_restrict_height && listHeight < bodyHeight){
15524 this.touchViewBodyEl.setHeight(listHeight);
15529 if(firstChecked && listHeight > bodyHeight){
15530 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15535 onTouchViewLoadException : function()
15537 this.hideTouchView();
15540 onTouchViewEmptyResults : function()
15542 this.clearTouchView();
15544 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15546 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15550 clearTouchView : function()
15552 this.touchViewListGroup.dom.innerHTML = '';
15555 onTouchViewClick : function(e, el, o)
15557 e.preventDefault();
15560 var rowIndex = o.rowIndex;
15562 var r = this.store.getAt(rowIndex);
15564 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15566 if(!this.multiple){
15567 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15568 c.dom.removeAttribute('checked');
15571 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15573 this.setFromData(r.data);
15575 var close = this.closeTriggerEl();
15581 this.hideTouchView();
15583 this.fireEvent('select', this, r, rowIndex);
15588 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15589 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15590 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15594 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15595 this.addItem(r.data);
15596 this.tickItems.push(r.data);
15600 getAutoCreateNativeIOS : function()
15603 cls: 'form-group' //input-group,
15608 cls : 'roo-ios-select'
15612 combobox.name = this.name;
15615 if (this.disabled) {
15616 combobox.disabled = true;
15619 var settings = this;
15621 ['xs','sm','md','lg'].map(function(size){
15622 if (settings[size]) {
15623 cfg.cls += ' col-' + size + '-' + settings[size];
15633 initIOSView : function()
15635 this.store.on('load', this.onIOSViewLoad, this);
15640 onIOSViewLoad : function()
15642 if(this.store.getCount() < 1){
15646 this.clearIOSView();
15648 if(this.allowBlank) {
15650 var default_text = '-- SELECT --';
15652 if(this.placeholder.length){
15653 default_text = this.placeholder;
15656 if(this.emptyTitle.length){
15657 default_text += ' - ' + this.emptyTitle + ' -';
15660 var opt = this.inputEl().createChild({
15663 html : default_text
15667 o[this.valueField] = 0;
15668 o[this.displayField] = default_text;
15670 this.ios_options.push({
15677 this.store.data.each(function(d, rowIndex){
15681 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15682 html = d.data[this.displayField];
15687 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15688 value = d.data[this.valueField];
15697 if(this.value == d.data[this.valueField]){
15698 option['selected'] = true;
15701 var opt = this.inputEl().createChild(option);
15703 this.ios_options.push({
15710 this.inputEl().on('change', function(){
15711 this.fireEvent('select', this);
15716 clearIOSView: function()
15718 this.inputEl().dom.innerHTML = '';
15720 this.ios_options = [];
15723 setIOSValue: function(v)
15727 if(!this.ios_options){
15731 Roo.each(this.ios_options, function(opts){
15733 opts.el.dom.removeAttribute('selected');
15735 if(opts.data[this.valueField] != v){
15739 opts.el.dom.setAttribute('selected', true);
15745 * @cfg {Boolean} grow
15749 * @cfg {Number} growMin
15753 * @cfg {Number} growMax
15762 Roo.apply(Roo.bootstrap.ComboBox, {
15766 cls: 'modal-header',
15788 cls: 'list-group-item',
15792 cls: 'roo-combobox-list-group-item-value'
15796 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15810 listItemCheckbox : {
15812 cls: 'list-group-item',
15816 cls: 'roo-combobox-list-group-item-value'
15820 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15836 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15841 cls: 'modal-footer',
15849 cls: 'col-xs-6 text-left',
15852 cls: 'btn btn-danger roo-touch-view-cancel',
15858 cls: 'col-xs-6 text-right',
15861 cls: 'btn btn-success roo-touch-view-ok',
15872 Roo.apply(Roo.bootstrap.ComboBox, {
15874 touchViewTemplate : {
15876 cls: 'modal fade roo-combobox-touch-view',
15880 cls: 'modal-dialog',
15881 style : 'position:fixed', // we have to fix position....
15885 cls: 'modal-content',
15887 Roo.bootstrap.ComboBox.header,
15888 Roo.bootstrap.ComboBox.body,
15889 Roo.bootstrap.ComboBox.footer
15898 * Ext JS Library 1.1.1
15899 * Copyright(c) 2006-2007, Ext JS, LLC.
15901 * Originally Released Under LGPL - original licence link has changed is not relivant.
15904 * <script type="text/javascript">
15909 * @extends Roo.util.Observable
15910 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15911 * This class also supports single and multi selection modes. <br>
15912 * Create a data model bound view:
15914 var store = new Roo.data.Store(...);
15916 var view = new Roo.View({
15918 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15920 singleSelect: true,
15921 selectedClass: "ydataview-selected",
15925 // listen for node click?
15926 view.on("click", function(vw, index, node, e){
15927 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15931 dataModel.load("foobar.xml");
15933 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15935 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15936 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15938 * Note: old style constructor is still suported (container, template, config)
15941 * Create a new View
15942 * @param {Object} config The config object
15945 Roo.View = function(config, depreciated_tpl, depreciated_config){
15947 this.parent = false;
15949 if (typeof(depreciated_tpl) == 'undefined') {
15950 // new way.. - universal constructor.
15951 Roo.apply(this, config);
15952 this.el = Roo.get(this.el);
15955 this.el = Roo.get(config);
15956 this.tpl = depreciated_tpl;
15957 Roo.apply(this, depreciated_config);
15959 this.wrapEl = this.el.wrap().wrap();
15960 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15963 if(typeof(this.tpl) == "string"){
15964 this.tpl = new Roo.Template(this.tpl);
15966 // support xtype ctors..
15967 this.tpl = new Roo.factory(this.tpl, Roo);
15971 this.tpl.compile();
15976 * @event beforeclick
15977 * Fires before a click is processed. Returns false to cancel the default action.
15978 * @param {Roo.View} this
15979 * @param {Number} index The index of the target node
15980 * @param {HTMLElement} node The target node
15981 * @param {Roo.EventObject} e The raw event object
15983 "beforeclick" : true,
15986 * Fires when a template node is clicked.
15987 * @param {Roo.View} this
15988 * @param {Number} index The index of the target node
15989 * @param {HTMLElement} node The target node
15990 * @param {Roo.EventObject} e The raw event object
15995 * Fires when a template node is double clicked.
15996 * @param {Roo.View} this
15997 * @param {Number} index The index of the target node
15998 * @param {HTMLElement} node The target node
15999 * @param {Roo.EventObject} e The raw event object
16003 * @event contextmenu
16004 * Fires when a template node is right clicked.
16005 * @param {Roo.View} this
16006 * @param {Number} index The index of the target node
16007 * @param {HTMLElement} node The target node
16008 * @param {Roo.EventObject} e The raw event object
16010 "contextmenu" : true,
16012 * @event selectionchange
16013 * Fires when the selected nodes change.
16014 * @param {Roo.View} this
16015 * @param {Array} selections Array of the selected nodes
16017 "selectionchange" : true,
16020 * @event beforeselect
16021 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16022 * @param {Roo.View} this
16023 * @param {HTMLElement} node The node to be selected
16024 * @param {Array} selections Array of currently selected nodes
16026 "beforeselect" : true,
16028 * @event preparedata
16029 * Fires on every row to render, to allow you to change the data.
16030 * @param {Roo.View} this
16031 * @param {Object} data to be rendered (change this)
16033 "preparedata" : true
16041 "click": this.onClick,
16042 "dblclick": this.onDblClick,
16043 "contextmenu": this.onContextMenu,
16047 this.selections = [];
16049 this.cmp = new Roo.CompositeElementLite([]);
16051 this.store = Roo.factory(this.store, Roo.data);
16052 this.setStore(this.store, true);
16055 if ( this.footer && this.footer.xtype) {
16057 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16059 this.footer.dataSource = this.store;
16060 this.footer.container = fctr;
16061 this.footer = Roo.factory(this.footer, Roo);
16062 fctr.insertFirst(this.el);
16064 // this is a bit insane - as the paging toolbar seems to detach the el..
16065 // dom.parentNode.parentNode.parentNode
16066 // they get detached?
16070 Roo.View.superclass.constructor.call(this);
16075 Roo.extend(Roo.View, Roo.util.Observable, {
16078 * @cfg {Roo.data.Store} store Data store to load data from.
16083 * @cfg {String|Roo.Element} el The container element.
16088 * @cfg {String|Roo.Template} tpl The template used by this View
16092 * @cfg {String} dataName the named area of the template to use as the data area
16093 * Works with domtemplates roo-name="name"
16097 * @cfg {String} selectedClass The css class to add to selected nodes
16099 selectedClass : "x-view-selected",
16101 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16106 * @cfg {String} text to display on mask (default Loading)
16110 * @cfg {Boolean} multiSelect Allow multiple selection
16112 multiSelect : false,
16114 * @cfg {Boolean} singleSelect Allow single selection
16116 singleSelect: false,
16119 * @cfg {Boolean} toggleSelect - selecting
16121 toggleSelect : false,
16124 * @cfg {Boolean} tickable - selecting
16129 * Returns the element this view is bound to.
16130 * @return {Roo.Element}
16132 getEl : function(){
16133 return this.wrapEl;
16139 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16141 refresh : function(){
16142 //Roo.log('refresh');
16145 // if we are using something like 'domtemplate', then
16146 // the what gets used is:
16147 // t.applySubtemplate(NAME, data, wrapping data..)
16148 // the outer template then get' applied with
16149 // the store 'extra data'
16150 // and the body get's added to the
16151 // roo-name="data" node?
16152 // <span class='roo-tpl-{name}'></span> ?????
16156 this.clearSelections();
16157 this.el.update("");
16159 var records = this.store.getRange();
16160 if(records.length < 1) {
16162 // is this valid?? = should it render a template??
16164 this.el.update(this.emptyText);
16168 if (this.dataName) {
16169 this.el.update(t.apply(this.store.meta)); //????
16170 el = this.el.child('.roo-tpl-' + this.dataName);
16173 for(var i = 0, len = records.length; i < len; i++){
16174 var data = this.prepareData(records[i].data, i, records[i]);
16175 this.fireEvent("preparedata", this, data, i, records[i]);
16177 var d = Roo.apply({}, data);
16180 Roo.apply(d, {'roo-id' : Roo.id()});
16184 Roo.each(this.parent.item, function(item){
16185 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16188 Roo.apply(d, {'roo-data-checked' : 'checked'});
16192 html[html.length] = Roo.util.Format.trim(
16194 t.applySubtemplate(this.dataName, d, this.store.meta) :
16201 el.update(html.join(""));
16202 this.nodes = el.dom.childNodes;
16203 this.updateIndexes(0);
16208 * Function to override to reformat the data that is sent to
16209 * the template for each node.
16210 * DEPRICATED - use the preparedata event handler.
16211 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16212 * a JSON object for an UpdateManager bound view).
16214 prepareData : function(data, index, record)
16216 this.fireEvent("preparedata", this, data, index, record);
16220 onUpdate : function(ds, record){
16221 // Roo.log('on update');
16222 this.clearSelections();
16223 var index = this.store.indexOf(record);
16224 var n = this.nodes[index];
16225 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16226 n.parentNode.removeChild(n);
16227 this.updateIndexes(index, index);
16233 onAdd : function(ds, records, index)
16235 //Roo.log(['on Add', ds, records, index] );
16236 this.clearSelections();
16237 if(this.nodes.length == 0){
16241 var n = this.nodes[index];
16242 for(var i = 0, len = records.length; i < len; i++){
16243 var d = this.prepareData(records[i].data, i, records[i]);
16245 this.tpl.insertBefore(n, d);
16248 this.tpl.append(this.el, d);
16251 this.updateIndexes(index);
16254 onRemove : function(ds, record, index){
16255 // Roo.log('onRemove');
16256 this.clearSelections();
16257 var el = this.dataName ?
16258 this.el.child('.roo-tpl-' + this.dataName) :
16261 el.dom.removeChild(this.nodes[index]);
16262 this.updateIndexes(index);
16266 * Refresh an individual node.
16267 * @param {Number} index
16269 refreshNode : function(index){
16270 this.onUpdate(this.store, this.store.getAt(index));
16273 updateIndexes : function(startIndex, endIndex){
16274 var ns = this.nodes;
16275 startIndex = startIndex || 0;
16276 endIndex = endIndex || ns.length - 1;
16277 for(var i = startIndex; i <= endIndex; i++){
16278 ns[i].nodeIndex = i;
16283 * Changes the data store this view uses and refresh the view.
16284 * @param {Store} store
16286 setStore : function(store, initial){
16287 if(!initial && this.store){
16288 this.store.un("datachanged", this.refresh);
16289 this.store.un("add", this.onAdd);
16290 this.store.un("remove", this.onRemove);
16291 this.store.un("update", this.onUpdate);
16292 this.store.un("clear", this.refresh);
16293 this.store.un("beforeload", this.onBeforeLoad);
16294 this.store.un("load", this.onLoad);
16295 this.store.un("loadexception", this.onLoad);
16299 store.on("datachanged", this.refresh, this);
16300 store.on("add", this.onAdd, this);
16301 store.on("remove", this.onRemove, this);
16302 store.on("update", this.onUpdate, this);
16303 store.on("clear", this.refresh, this);
16304 store.on("beforeload", this.onBeforeLoad, this);
16305 store.on("load", this.onLoad, this);
16306 store.on("loadexception", this.onLoad, this);
16314 * onbeforeLoad - masks the loading area.
16317 onBeforeLoad : function(store,opts)
16319 //Roo.log('onBeforeLoad');
16321 this.el.update("");
16323 this.el.mask(this.mask ? this.mask : "Loading" );
16325 onLoad : function ()
16332 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16333 * @param {HTMLElement} node
16334 * @return {HTMLElement} The template node
16336 findItemFromChild : function(node){
16337 var el = this.dataName ?
16338 this.el.child('.roo-tpl-' + this.dataName,true) :
16341 if(!node || node.parentNode == el){
16344 var p = node.parentNode;
16345 while(p && p != el){
16346 if(p.parentNode == el){
16355 onClick : function(e){
16356 var item = this.findItemFromChild(e.getTarget());
16358 var index = this.indexOf(item);
16359 if(this.onItemClick(item, index, e) !== false){
16360 this.fireEvent("click", this, index, item, e);
16363 this.clearSelections();
16368 onContextMenu : function(e){
16369 var item = this.findItemFromChild(e.getTarget());
16371 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16376 onDblClick : function(e){
16377 var item = this.findItemFromChild(e.getTarget());
16379 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16383 onItemClick : function(item, index, e)
16385 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16388 if (this.toggleSelect) {
16389 var m = this.isSelected(item) ? 'unselect' : 'select';
16392 _t[m](item, true, false);
16395 if(this.multiSelect || this.singleSelect){
16396 if(this.multiSelect && e.shiftKey && this.lastSelection){
16397 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16399 this.select(item, this.multiSelect && e.ctrlKey);
16400 this.lastSelection = item;
16403 if(!this.tickable){
16404 e.preventDefault();
16412 * Get the number of selected nodes.
16415 getSelectionCount : function(){
16416 return this.selections.length;
16420 * Get the currently selected nodes.
16421 * @return {Array} An array of HTMLElements
16423 getSelectedNodes : function(){
16424 return this.selections;
16428 * Get the indexes of the selected nodes.
16431 getSelectedIndexes : function(){
16432 var indexes = [], s = this.selections;
16433 for(var i = 0, len = s.length; i < len; i++){
16434 indexes.push(s[i].nodeIndex);
16440 * Clear all selections
16441 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16443 clearSelections : function(suppressEvent){
16444 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16445 this.cmp.elements = this.selections;
16446 this.cmp.removeClass(this.selectedClass);
16447 this.selections = [];
16448 if(!suppressEvent){
16449 this.fireEvent("selectionchange", this, this.selections);
16455 * Returns true if the passed node is selected
16456 * @param {HTMLElement/Number} node The node or node index
16457 * @return {Boolean}
16459 isSelected : function(node){
16460 var s = this.selections;
16464 node = this.getNode(node);
16465 return s.indexOf(node) !== -1;
16470 * @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
16471 * @param {Boolean} keepExisting (optional) true to keep existing selections
16472 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16474 select : function(nodeInfo, keepExisting, suppressEvent){
16475 if(nodeInfo instanceof Array){
16477 this.clearSelections(true);
16479 for(var i = 0, len = nodeInfo.length; i < len; i++){
16480 this.select(nodeInfo[i], true, true);
16484 var node = this.getNode(nodeInfo);
16485 if(!node || this.isSelected(node)){
16486 return; // already selected.
16489 this.clearSelections(true);
16492 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16493 Roo.fly(node).addClass(this.selectedClass);
16494 this.selections.push(node);
16495 if(!suppressEvent){
16496 this.fireEvent("selectionchange", this, this.selections);
16504 * @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
16505 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16506 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16508 unselect : function(nodeInfo, keepExisting, suppressEvent)
16510 if(nodeInfo instanceof Array){
16511 Roo.each(this.selections, function(s) {
16512 this.unselect(s, nodeInfo);
16516 var node = this.getNode(nodeInfo);
16517 if(!node || !this.isSelected(node)){
16518 //Roo.log("not selected");
16519 return; // not selected.
16523 Roo.each(this.selections, function(s) {
16525 Roo.fly(node).removeClass(this.selectedClass);
16532 this.selections= ns;
16533 this.fireEvent("selectionchange", this, this.selections);
16537 * Gets a template node.
16538 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16539 * @return {HTMLElement} The node or null if it wasn't found
16541 getNode : function(nodeInfo){
16542 if(typeof nodeInfo == "string"){
16543 return document.getElementById(nodeInfo);
16544 }else if(typeof nodeInfo == "number"){
16545 return this.nodes[nodeInfo];
16551 * Gets a range template nodes.
16552 * @param {Number} startIndex
16553 * @param {Number} endIndex
16554 * @return {Array} An array of nodes
16556 getNodes : function(start, end){
16557 var ns = this.nodes;
16558 start = start || 0;
16559 end = typeof end == "undefined" ? ns.length - 1 : end;
16562 for(var i = start; i <= end; i++){
16566 for(var i = start; i >= end; i--){
16574 * Finds the index of the passed node
16575 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16576 * @return {Number} The index of the node or -1
16578 indexOf : function(node){
16579 node = this.getNode(node);
16580 if(typeof node.nodeIndex == "number"){
16581 return node.nodeIndex;
16583 var ns = this.nodes;
16584 for(var i = 0, len = ns.length; i < len; i++){
16595 * based on jquery fullcalendar
16599 Roo.bootstrap = Roo.bootstrap || {};
16601 * @class Roo.bootstrap.Calendar
16602 * @extends Roo.bootstrap.Component
16603 * Bootstrap Calendar class
16604 * @cfg {Boolean} loadMask (true|false) default false
16605 * @cfg {Object} header generate the user specific header of the calendar, default false
16608 * Create a new Container
16609 * @param {Object} config The config object
16614 Roo.bootstrap.Calendar = function(config){
16615 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16619 * Fires when a date is selected
16620 * @param {DatePicker} this
16621 * @param {Date} date The selected date
16625 * @event monthchange
16626 * Fires when the displayed month changes
16627 * @param {DatePicker} this
16628 * @param {Date} date The selected month
16630 'monthchange': true,
16632 * @event evententer
16633 * Fires when mouse over an event
16634 * @param {Calendar} this
16635 * @param {event} Event
16637 'evententer': true,
16639 * @event eventleave
16640 * Fires when the mouse leaves an
16641 * @param {Calendar} this
16644 'eventleave': true,
16646 * @event eventclick
16647 * Fires when the mouse click an
16648 * @param {Calendar} this
16657 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16660 * @cfg {Number} startDay
16661 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16669 getAutoCreate : function(){
16672 var fc_button = function(name, corner, style, content ) {
16673 return Roo.apply({},{
16675 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16677 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16680 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16691 style : 'width:100%',
16698 cls : 'fc-header-left',
16700 fc_button('prev', 'left', 'arrow', '‹' ),
16701 fc_button('next', 'right', 'arrow', '›' ),
16702 { tag: 'span', cls: 'fc-header-space' },
16703 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16711 cls : 'fc-header-center',
16715 cls: 'fc-header-title',
16718 html : 'month / year'
16726 cls : 'fc-header-right',
16728 /* fc_button('month', 'left', '', 'month' ),
16729 fc_button('week', '', '', 'week' ),
16730 fc_button('day', 'right', '', 'day' )
16742 header = this.header;
16745 var cal_heads = function() {
16747 // fixme - handle this.
16749 for (var i =0; i < Date.dayNames.length; i++) {
16750 var d = Date.dayNames[i];
16753 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16754 html : d.substring(0,3)
16758 ret[0].cls += ' fc-first';
16759 ret[6].cls += ' fc-last';
16762 var cal_cell = function(n) {
16765 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16770 cls: 'fc-day-number',
16774 cls: 'fc-day-content',
16778 style: 'position: relative;' // height: 17px;
16790 var cal_rows = function() {
16793 for (var r = 0; r < 6; r++) {
16800 for (var i =0; i < Date.dayNames.length; i++) {
16801 var d = Date.dayNames[i];
16802 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16805 row.cn[0].cls+=' fc-first';
16806 row.cn[0].cn[0].style = 'min-height:90px';
16807 row.cn[6].cls+=' fc-last';
16811 ret[0].cls += ' fc-first';
16812 ret[4].cls += ' fc-prev-last';
16813 ret[5].cls += ' fc-last';
16820 cls: 'fc-border-separate',
16821 style : 'width:100%',
16829 cls : 'fc-first fc-last',
16847 cls : 'fc-content',
16848 style : "position: relative;",
16851 cls : 'fc-view fc-view-month fc-grid',
16852 style : 'position: relative',
16853 unselectable : 'on',
16856 cls : 'fc-event-container',
16857 style : 'position:absolute;z-index:8;top:0;left:0;'
16875 initEvents : function()
16878 throw "can not find store for calendar";
16884 style: "text-align:center",
16888 style: "background-color:white;width:50%;margin:250 auto",
16892 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16903 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16905 var size = this.el.select('.fc-content', true).first().getSize();
16906 this.maskEl.setSize(size.width, size.height);
16907 this.maskEl.enableDisplayMode("block");
16908 if(!this.loadMask){
16909 this.maskEl.hide();
16912 this.store = Roo.factory(this.store, Roo.data);
16913 this.store.on('load', this.onLoad, this);
16914 this.store.on('beforeload', this.onBeforeLoad, this);
16918 this.cells = this.el.select('.fc-day',true);
16919 //Roo.log(this.cells);
16920 this.textNodes = this.el.query('.fc-day-number');
16921 this.cells.addClassOnOver('fc-state-hover');
16923 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16924 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16925 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16926 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16928 this.on('monthchange', this.onMonthChange, this);
16930 this.update(new Date().clearTime());
16933 resize : function() {
16934 var sz = this.el.getSize();
16936 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16937 this.el.select('.fc-day-content div',true).setHeight(34);
16942 showPrevMonth : function(e){
16943 this.update(this.activeDate.add("mo", -1));
16945 showToday : function(e){
16946 this.update(new Date().clearTime());
16949 showNextMonth : function(e){
16950 this.update(this.activeDate.add("mo", 1));
16954 showPrevYear : function(){
16955 this.update(this.activeDate.add("y", -1));
16959 showNextYear : function(){
16960 this.update(this.activeDate.add("y", 1));
16965 update : function(date)
16967 var vd = this.activeDate;
16968 this.activeDate = date;
16969 // if(vd && this.el){
16970 // var t = date.getTime();
16971 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16972 // Roo.log('using add remove');
16974 // this.fireEvent('monthchange', this, date);
16976 // this.cells.removeClass("fc-state-highlight");
16977 // this.cells.each(function(c){
16978 // if(c.dateValue == t){
16979 // c.addClass("fc-state-highlight");
16980 // setTimeout(function(){
16981 // try{c.dom.firstChild.focus();}catch(e){}
16991 var days = date.getDaysInMonth();
16993 var firstOfMonth = date.getFirstDateOfMonth();
16994 var startingPos = firstOfMonth.getDay()-this.startDay;
16996 if(startingPos < this.startDay){
17000 var pm = date.add(Date.MONTH, -1);
17001 var prevStart = pm.getDaysInMonth()-startingPos;
17003 this.cells = this.el.select('.fc-day',true);
17004 this.textNodes = this.el.query('.fc-day-number');
17005 this.cells.addClassOnOver('fc-state-hover');
17007 var cells = this.cells.elements;
17008 var textEls = this.textNodes;
17010 Roo.each(cells, function(cell){
17011 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17014 days += startingPos;
17016 // convert everything to numbers so it's fast
17017 var day = 86400000;
17018 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17021 //Roo.log(prevStart);
17023 var today = new Date().clearTime().getTime();
17024 var sel = date.clearTime().getTime();
17025 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17026 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17027 var ddMatch = this.disabledDatesRE;
17028 var ddText = this.disabledDatesText;
17029 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17030 var ddaysText = this.disabledDaysText;
17031 var format = this.format;
17033 var setCellClass = function(cal, cell){
17037 //Roo.log('set Cell Class');
17039 var t = d.getTime();
17043 cell.dateValue = t;
17045 cell.className += " fc-today";
17046 cell.className += " fc-state-highlight";
17047 cell.title = cal.todayText;
17050 // disable highlight in other month..
17051 //cell.className += " fc-state-highlight";
17056 cell.className = " fc-state-disabled";
17057 cell.title = cal.minText;
17061 cell.className = " fc-state-disabled";
17062 cell.title = cal.maxText;
17066 if(ddays.indexOf(d.getDay()) != -1){
17067 cell.title = ddaysText;
17068 cell.className = " fc-state-disabled";
17071 if(ddMatch && format){
17072 var fvalue = d.dateFormat(format);
17073 if(ddMatch.test(fvalue)){
17074 cell.title = ddText.replace("%0", fvalue);
17075 cell.className = " fc-state-disabled";
17079 if (!cell.initialClassName) {
17080 cell.initialClassName = cell.dom.className;
17083 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17088 for(; i < startingPos; i++) {
17089 textEls[i].innerHTML = (++prevStart);
17090 d.setDate(d.getDate()+1);
17092 cells[i].className = "fc-past fc-other-month";
17093 setCellClass(this, cells[i]);
17098 for(; i < days; i++){
17099 intDay = i - startingPos + 1;
17100 textEls[i].innerHTML = (intDay);
17101 d.setDate(d.getDate()+1);
17103 cells[i].className = ''; // "x-date-active";
17104 setCellClass(this, cells[i]);
17108 for(; i < 42; i++) {
17109 textEls[i].innerHTML = (++extraDays);
17110 d.setDate(d.getDate()+1);
17112 cells[i].className = "fc-future fc-other-month";
17113 setCellClass(this, cells[i]);
17116 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17118 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17120 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17121 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17123 if(totalRows != 6){
17124 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17125 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17128 this.fireEvent('monthchange', this, date);
17132 if(!this.internalRender){
17133 var main = this.el.dom.firstChild;
17134 var w = main.offsetWidth;
17135 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17136 Roo.fly(main).setWidth(w);
17137 this.internalRender = true;
17138 // opera does not respect the auto grow header center column
17139 // then, after it gets a width opera refuses to recalculate
17140 // without a second pass
17141 if(Roo.isOpera && !this.secondPass){
17142 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17143 this.secondPass = true;
17144 this.update.defer(10, this, [date]);
17151 findCell : function(dt) {
17152 dt = dt.clearTime().getTime();
17154 this.cells.each(function(c){
17155 //Roo.log("check " +c.dateValue + '?=' + dt);
17156 if(c.dateValue == dt){
17166 findCells : function(ev) {
17167 var s = ev.start.clone().clearTime().getTime();
17169 var e= ev.end.clone().clearTime().getTime();
17172 this.cells.each(function(c){
17173 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17175 if(c.dateValue > e){
17178 if(c.dateValue < s){
17187 // findBestRow: function(cells)
17191 // for (var i =0 ; i < cells.length;i++) {
17192 // ret = Math.max(cells[i].rows || 0,ret);
17199 addItem : function(ev)
17201 // look for vertical location slot in
17202 var cells = this.findCells(ev);
17204 // ev.row = this.findBestRow(cells);
17206 // work out the location.
17210 for(var i =0; i < cells.length; i++) {
17212 cells[i].row = cells[0].row;
17215 cells[i].row = cells[i].row + 1;
17225 if (crow.start.getY() == cells[i].getY()) {
17227 crow.end = cells[i];
17244 cells[0].events.push(ev);
17246 this.calevents.push(ev);
17249 clearEvents: function() {
17251 if(!this.calevents){
17255 Roo.each(this.cells.elements, function(c){
17261 Roo.each(this.calevents, function(e) {
17262 Roo.each(e.els, function(el) {
17263 el.un('mouseenter' ,this.onEventEnter, this);
17264 el.un('mouseleave' ,this.onEventLeave, this);
17269 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17275 renderEvents: function()
17279 this.cells.each(function(c) {
17288 if(c.row != c.events.length){
17289 r = 4 - (4 - (c.row - c.events.length));
17292 c.events = ev.slice(0, r);
17293 c.more = ev.slice(r);
17295 if(c.more.length && c.more.length == 1){
17296 c.events.push(c.more.pop());
17299 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17303 this.cells.each(function(c) {
17305 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17308 for (var e = 0; e < c.events.length; e++){
17309 var ev = c.events[e];
17310 var rows = ev.rows;
17312 for(var i = 0; i < rows.length; i++) {
17314 // how many rows should it span..
17317 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17318 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17320 unselectable : "on",
17323 cls: 'fc-event-inner',
17327 // cls: 'fc-event-time',
17328 // html : cells.length > 1 ? '' : ev.time
17332 cls: 'fc-event-title',
17333 html : String.format('{0}', ev.title)
17340 cls: 'ui-resizable-handle ui-resizable-e',
17341 html : '  '
17348 cfg.cls += ' fc-event-start';
17350 if ((i+1) == rows.length) {
17351 cfg.cls += ' fc-event-end';
17354 var ctr = _this.el.select('.fc-event-container',true).first();
17355 var cg = ctr.createChild(cfg);
17357 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17358 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17360 var r = (c.more.length) ? 1 : 0;
17361 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17362 cg.setWidth(ebox.right - sbox.x -2);
17364 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17365 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17366 cg.on('click', _this.onEventClick, _this, ev);
17377 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17378 style : 'position: absolute',
17379 unselectable : "on",
17382 cls: 'fc-event-inner',
17386 cls: 'fc-event-title',
17394 cls: 'ui-resizable-handle ui-resizable-e',
17395 html : '  '
17401 var ctr = _this.el.select('.fc-event-container',true).first();
17402 var cg = ctr.createChild(cfg);
17404 var sbox = c.select('.fc-day-content',true).first().getBox();
17405 var ebox = c.select('.fc-day-content',true).first().getBox();
17407 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17408 cg.setWidth(ebox.right - sbox.x -2);
17410 cg.on('click', _this.onMoreEventClick, _this, c.more);
17420 onEventEnter: function (e, el,event,d) {
17421 this.fireEvent('evententer', this, el, event);
17424 onEventLeave: function (e, el,event,d) {
17425 this.fireEvent('eventleave', this, el, event);
17428 onEventClick: function (e, el,event,d) {
17429 this.fireEvent('eventclick', this, el, event);
17432 onMonthChange: function () {
17436 onMoreEventClick: function(e, el, more)
17440 this.calpopover.placement = 'right';
17441 this.calpopover.setTitle('More');
17443 this.calpopover.setContent('');
17445 var ctr = this.calpopover.el.select('.popover-content', true).first();
17447 Roo.each(more, function(m){
17449 cls : 'fc-event-hori fc-event-draggable',
17452 var cg = ctr.createChild(cfg);
17454 cg.on('click', _this.onEventClick, _this, m);
17457 this.calpopover.show(el);
17462 onLoad: function ()
17464 this.calevents = [];
17467 if(this.store.getCount() > 0){
17468 this.store.data.each(function(d){
17471 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17472 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17473 time : d.data.start_time,
17474 title : d.data.title,
17475 description : d.data.description,
17476 venue : d.data.venue
17481 this.renderEvents();
17483 if(this.calevents.length && this.loadMask){
17484 this.maskEl.hide();
17488 onBeforeLoad: function()
17490 this.clearEvents();
17492 this.maskEl.show();
17506 * @class Roo.bootstrap.Popover
17507 * @extends Roo.bootstrap.Component
17508 * Bootstrap Popover class
17509 * @cfg {String} html contents of the popover (or false to use children..)
17510 * @cfg {String} title of popover (or false to hide)
17511 * @cfg {String} placement how it is placed
17512 * @cfg {String} trigger click || hover (or false to trigger manually)
17513 * @cfg {String} over what (parent or false to trigger manually.)
17514 * @cfg {Number} delay - delay before showing
17517 * Create a new Popover
17518 * @param {Object} config The config object
17521 Roo.bootstrap.Popover = function(config){
17522 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17528 * After the popover show
17530 * @param {Roo.bootstrap.Popover} this
17535 * After the popover hide
17537 * @param {Roo.bootstrap.Popover} this
17543 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17545 title: 'Fill in a title',
17548 placement : 'right',
17549 trigger : 'hover', // hover
17555 can_build_overlaid : false,
17557 getChildContainer : function()
17559 return this.el.select('.popover-content',true).first();
17562 getAutoCreate : function(){
17565 cls : 'popover roo-dynamic',
17566 style: 'display:block',
17572 cls : 'popover-inner',
17576 cls: 'popover-title',
17580 cls : 'popover-content',
17591 setTitle: function(str)
17594 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17596 setContent: function(str)
17599 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17601 // as it get's added to the bottom of the page.
17602 onRender : function(ct, position)
17604 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17606 var cfg = Roo.apply({}, this.getAutoCreate());
17610 cfg.cls += ' ' + this.cls;
17613 cfg.style = this.style;
17615 //Roo.log("adding to ");
17616 this.el = Roo.get(document.body).createChild(cfg, position);
17617 // Roo.log(this.el);
17622 initEvents : function()
17624 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17625 this.el.enableDisplayMode('block');
17627 if (this.over === false) {
17630 if (this.triggers === false) {
17633 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17634 var triggers = this.trigger ? this.trigger.split(' ') : [];
17635 Roo.each(triggers, function(trigger) {
17637 if (trigger == 'click') {
17638 on_el.on('click', this.toggle, this);
17639 } else if (trigger != 'manual') {
17640 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17641 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17643 on_el.on(eventIn ,this.enter, this);
17644 on_el.on(eventOut, this.leave, this);
17655 toggle : function () {
17656 this.hoverState == 'in' ? this.leave() : this.enter();
17659 enter : function () {
17661 clearTimeout(this.timeout);
17663 this.hoverState = 'in';
17665 if (!this.delay || !this.delay.show) {
17670 this.timeout = setTimeout(function () {
17671 if (_t.hoverState == 'in') {
17674 }, this.delay.show)
17677 leave : function() {
17678 clearTimeout(this.timeout);
17680 this.hoverState = 'out';
17682 if (!this.delay || !this.delay.hide) {
17687 this.timeout = setTimeout(function () {
17688 if (_t.hoverState == 'out') {
17691 }, this.delay.hide)
17694 show : function (on_el)
17697 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17701 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17702 if (this.html !== false) {
17703 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17705 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17706 if (!this.title.length) {
17707 this.el.select('.popover-title',true).hide();
17710 var placement = typeof this.placement == 'function' ?
17711 this.placement.call(this, this.el, on_el) :
17714 var autoToken = /\s?auto?\s?/i;
17715 var autoPlace = autoToken.test(placement);
17717 placement = placement.replace(autoToken, '') || 'top';
17721 //this.el.setXY([0,0]);
17723 this.el.dom.style.display='block';
17724 this.el.addClass(placement);
17726 //this.el.appendTo(on_el);
17728 var p = this.getPosition();
17729 var box = this.el.getBox();
17734 var align = Roo.bootstrap.Popover.alignment[placement];
17737 this.el.alignTo(on_el, align[0],align[1]);
17738 //var arrow = this.el.select('.arrow',true).first();
17739 //arrow.set(align[2],
17741 this.el.addClass('in');
17744 if (this.el.hasClass('fade')) {
17748 this.hoverState = 'in';
17750 this.fireEvent('show', this);
17755 this.el.setXY([0,0]);
17756 this.el.removeClass('in');
17758 this.hoverState = null;
17760 this.fireEvent('hide', this);
17765 Roo.bootstrap.Popover.alignment = {
17766 'left' : ['r-l', [-10,0], 'right'],
17767 'right' : ['l-r', [10,0], 'left'],
17768 'bottom' : ['t-b', [0,10], 'top'],
17769 'top' : [ 'b-t', [0,-10], 'bottom']
17780 * @class Roo.bootstrap.Progress
17781 * @extends Roo.bootstrap.Component
17782 * Bootstrap Progress class
17783 * @cfg {Boolean} striped striped of the progress bar
17784 * @cfg {Boolean} active animated of the progress bar
17788 * Create a new Progress
17789 * @param {Object} config The config object
17792 Roo.bootstrap.Progress = function(config){
17793 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17796 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17801 getAutoCreate : function(){
17809 cfg.cls += ' progress-striped';
17813 cfg.cls += ' active';
17832 * @class Roo.bootstrap.ProgressBar
17833 * @extends Roo.bootstrap.Component
17834 * Bootstrap ProgressBar class
17835 * @cfg {Number} aria_valuenow aria-value now
17836 * @cfg {Number} aria_valuemin aria-value min
17837 * @cfg {Number} aria_valuemax aria-value max
17838 * @cfg {String} label label for the progress bar
17839 * @cfg {String} panel (success | info | warning | danger )
17840 * @cfg {String} role role of the progress bar
17841 * @cfg {String} sr_only text
17845 * Create a new ProgressBar
17846 * @param {Object} config The config object
17849 Roo.bootstrap.ProgressBar = function(config){
17850 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17853 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17857 aria_valuemax : 100,
17863 getAutoCreate : function()
17868 cls: 'progress-bar',
17869 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17881 cfg.role = this.role;
17884 if(this.aria_valuenow){
17885 cfg['aria-valuenow'] = this.aria_valuenow;
17888 if(this.aria_valuemin){
17889 cfg['aria-valuemin'] = this.aria_valuemin;
17892 if(this.aria_valuemax){
17893 cfg['aria-valuemax'] = this.aria_valuemax;
17896 if(this.label && !this.sr_only){
17897 cfg.html = this.label;
17901 cfg.cls += ' progress-bar-' + this.panel;
17907 update : function(aria_valuenow)
17909 this.aria_valuenow = aria_valuenow;
17911 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17926 * @class Roo.bootstrap.TabGroup
17927 * @extends Roo.bootstrap.Column
17928 * Bootstrap Column class
17929 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17930 * @cfg {Boolean} carousel true to make the group behave like a carousel
17931 * @cfg {Boolean} bullets show bullets for the panels
17932 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17933 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17934 * @cfg {Boolean} showarrow (true|false) show arrow default true
17937 * Create a new TabGroup
17938 * @param {Object} config The config object
17941 Roo.bootstrap.TabGroup = function(config){
17942 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17944 this.navId = Roo.id();
17947 Roo.bootstrap.TabGroup.register(this);
17951 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17954 transition : false,
17959 slideOnTouch : false,
17962 getAutoCreate : function()
17964 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17966 cfg.cls += ' tab-content';
17968 if (this.carousel) {
17969 cfg.cls += ' carousel slide';
17972 cls : 'carousel-inner',
17976 if(this.bullets && !Roo.isTouch){
17979 cls : 'carousel-bullets',
17983 if(this.bullets_cls){
17984 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17991 cfg.cn[0].cn.push(bullets);
17994 if(this.showarrow){
17995 cfg.cn[0].cn.push({
17997 class : 'carousel-arrow',
18001 class : 'carousel-prev',
18005 class : 'fa fa-chevron-left'
18011 class : 'carousel-next',
18015 class : 'fa fa-chevron-right'
18028 initEvents: function()
18030 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18031 // this.el.on("touchstart", this.onTouchStart, this);
18034 if(this.autoslide){
18037 this.slideFn = window.setInterval(function() {
18038 _this.showPanelNext();
18042 if(this.showarrow){
18043 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18044 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18050 // onTouchStart : function(e, el, o)
18052 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18056 // this.showPanelNext();
18060 getChildContainer : function()
18062 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18066 * register a Navigation item
18067 * @param {Roo.bootstrap.NavItem} the navitem to add
18069 register : function(item)
18071 this.tabs.push( item);
18072 item.navId = this.navId; // not really needed..
18077 getActivePanel : function()
18080 Roo.each(this.tabs, function(t) {
18090 getPanelByName : function(n)
18093 Roo.each(this.tabs, function(t) {
18094 if (t.tabId == n) {
18102 indexOfPanel : function(p)
18105 Roo.each(this.tabs, function(t,i) {
18106 if (t.tabId == p.tabId) {
18115 * show a specific panel
18116 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18117 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18119 showPanel : function (pan)
18121 if(this.transition || typeof(pan) == 'undefined'){
18122 Roo.log("waiting for the transitionend");
18126 if (typeof(pan) == 'number') {
18127 pan = this.tabs[pan];
18130 if (typeof(pan) == 'string') {
18131 pan = this.getPanelByName(pan);
18134 var cur = this.getActivePanel();
18137 Roo.log('pan or acitve pan is undefined');
18141 if (pan.tabId == this.getActivePanel().tabId) {
18145 if (false === cur.fireEvent('beforedeactivate')) {
18149 if(this.bullets > 0 && !Roo.isTouch){
18150 this.setActiveBullet(this.indexOfPanel(pan));
18153 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18155 this.transition = true;
18156 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18157 var lr = dir == 'next' ? 'left' : 'right';
18158 pan.el.addClass(dir); // or prev
18159 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18160 cur.el.addClass(lr); // or right
18161 pan.el.addClass(lr);
18164 cur.el.on('transitionend', function() {
18165 Roo.log("trans end?");
18167 pan.el.removeClass([lr,dir]);
18168 pan.setActive(true);
18170 cur.el.removeClass([lr]);
18171 cur.setActive(false);
18173 _this.transition = false;
18175 }, this, { single: true } );
18180 cur.setActive(false);
18181 pan.setActive(true);
18186 showPanelNext : function()
18188 var i = this.indexOfPanel(this.getActivePanel());
18190 if (i >= this.tabs.length - 1 && !this.autoslide) {
18194 if (i >= this.tabs.length - 1 && this.autoslide) {
18198 this.showPanel(this.tabs[i+1]);
18201 showPanelPrev : function()
18203 var i = this.indexOfPanel(this.getActivePanel());
18205 if (i < 1 && !this.autoslide) {
18209 if (i < 1 && this.autoslide) {
18210 i = this.tabs.length;
18213 this.showPanel(this.tabs[i-1]);
18217 addBullet: function()
18219 if(!this.bullets || Roo.isTouch){
18222 var ctr = this.el.select('.carousel-bullets',true).first();
18223 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18224 var bullet = ctr.createChild({
18225 cls : 'bullet bullet-' + i
18226 },ctr.dom.lastChild);
18231 bullet.on('click', (function(e, el, o, ii, t){
18233 e.preventDefault();
18235 this.showPanel(ii);
18237 if(this.autoslide && this.slideFn){
18238 clearInterval(this.slideFn);
18239 this.slideFn = window.setInterval(function() {
18240 _this.showPanelNext();
18244 }).createDelegate(this, [i, bullet], true));
18249 setActiveBullet : function(i)
18255 Roo.each(this.el.select('.bullet', true).elements, function(el){
18256 el.removeClass('selected');
18259 var bullet = this.el.select('.bullet-' + i, true).first();
18265 bullet.addClass('selected');
18276 Roo.apply(Roo.bootstrap.TabGroup, {
18280 * register a Navigation Group
18281 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18283 register : function(navgrp)
18285 this.groups[navgrp.navId] = navgrp;
18289 * fetch a Navigation Group based on the navigation ID
18290 * if one does not exist , it will get created.
18291 * @param {string} the navgroup to add
18292 * @returns {Roo.bootstrap.NavGroup} the navgroup
18294 get: function(navId) {
18295 if (typeof(this.groups[navId]) == 'undefined') {
18296 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18298 return this.groups[navId] ;
18313 * @class Roo.bootstrap.TabPanel
18314 * @extends Roo.bootstrap.Component
18315 * Bootstrap TabPanel class
18316 * @cfg {Boolean} active panel active
18317 * @cfg {String} html panel content
18318 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18319 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18320 * @cfg {String} href click to link..
18324 * Create a new TabPanel
18325 * @param {Object} config The config object
18328 Roo.bootstrap.TabPanel = function(config){
18329 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18333 * Fires when the active status changes
18334 * @param {Roo.bootstrap.TabPanel} this
18335 * @param {Boolean} state the new state
18340 * @event beforedeactivate
18341 * Fires before a tab is de-activated - can be used to do validation on a form.
18342 * @param {Roo.bootstrap.TabPanel} this
18343 * @return {Boolean} false if there is an error
18346 'beforedeactivate': true
18349 this.tabId = this.tabId || Roo.id();
18353 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18361 getAutoCreate : function(){
18364 // item is needed for carousel - not sure if it has any effect otherwise
18365 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18366 html: this.html || ''
18370 cfg.cls += ' active';
18374 cfg.tabId = this.tabId;
18381 initEvents: function()
18383 var p = this.parent();
18385 this.navId = this.navId || p.navId;
18387 if (typeof(this.navId) != 'undefined') {
18388 // not really needed.. but just in case.. parent should be a NavGroup.
18389 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18393 var i = tg.tabs.length - 1;
18395 if(this.active && tg.bullets > 0 && i < tg.bullets){
18396 tg.setActiveBullet(i);
18400 this.el.on('click', this.onClick, this);
18403 this.el.on("touchstart", this.onTouchStart, this);
18404 this.el.on("touchmove", this.onTouchMove, this);
18405 this.el.on("touchend", this.onTouchEnd, this);
18410 onRender : function(ct, position)
18412 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18415 setActive : function(state)
18417 Roo.log("panel - set active " + this.tabId + "=" + state);
18419 this.active = state;
18421 this.el.removeClass('active');
18423 } else if (!this.el.hasClass('active')) {
18424 this.el.addClass('active');
18427 this.fireEvent('changed', this, state);
18430 onClick : function(e)
18432 e.preventDefault();
18434 if(!this.href.length){
18438 window.location.href = this.href;
18447 onTouchStart : function(e)
18449 this.swiping = false;
18451 this.startX = e.browserEvent.touches[0].clientX;
18452 this.startY = e.browserEvent.touches[0].clientY;
18455 onTouchMove : function(e)
18457 this.swiping = true;
18459 this.endX = e.browserEvent.touches[0].clientX;
18460 this.endY = e.browserEvent.touches[0].clientY;
18463 onTouchEnd : function(e)
18470 var tabGroup = this.parent();
18472 if(this.endX > this.startX){ // swiping right
18473 tabGroup.showPanelPrev();
18477 if(this.startX > this.endX){ // swiping left
18478 tabGroup.showPanelNext();
18497 * @class Roo.bootstrap.DateField
18498 * @extends Roo.bootstrap.Input
18499 * Bootstrap DateField class
18500 * @cfg {Number} weekStart default 0
18501 * @cfg {String} viewMode default empty, (months|years)
18502 * @cfg {String} minViewMode default empty, (months|years)
18503 * @cfg {Number} startDate default -Infinity
18504 * @cfg {Number} endDate default Infinity
18505 * @cfg {Boolean} todayHighlight default false
18506 * @cfg {Boolean} todayBtn default false
18507 * @cfg {Boolean} calendarWeeks default false
18508 * @cfg {Object} daysOfWeekDisabled default empty
18509 * @cfg {Boolean} singleMode default false (true | false)
18511 * @cfg {Boolean} keyboardNavigation default true
18512 * @cfg {String} language default en
18515 * Create a new DateField
18516 * @param {Object} config The config object
18519 Roo.bootstrap.DateField = function(config){
18520 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18524 * Fires when this field show.
18525 * @param {Roo.bootstrap.DateField} this
18526 * @param {Mixed} date The date value
18531 * Fires when this field hide.
18532 * @param {Roo.bootstrap.DateField} this
18533 * @param {Mixed} date The date value
18538 * Fires when select a date.
18539 * @param {Roo.bootstrap.DateField} this
18540 * @param {Mixed} date The date value
18544 * @event beforeselect
18545 * Fires when before select a date.
18546 * @param {Roo.bootstrap.DateField} this
18547 * @param {Mixed} date The date value
18549 beforeselect : true
18553 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18556 * @cfg {String} format
18557 * The default date format string which can be overriden for localization support. The format must be
18558 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18562 * @cfg {String} altFormats
18563 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18564 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18566 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18574 todayHighlight : false,
18580 keyboardNavigation: true,
18582 calendarWeeks: false,
18584 startDate: -Infinity,
18588 daysOfWeekDisabled: [],
18592 singleMode : false,
18594 UTCDate: function()
18596 return new Date(Date.UTC.apply(Date, arguments));
18599 UTCToday: function()
18601 var today = new Date();
18602 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18605 getDate: function() {
18606 var d = this.getUTCDate();
18607 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18610 getUTCDate: function() {
18614 setDate: function(d) {
18615 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18618 setUTCDate: function(d) {
18620 this.setValue(this.formatDate(this.date));
18623 onRender: function(ct, position)
18626 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18628 this.language = this.language || 'en';
18629 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18630 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18632 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18633 this.format = this.format || 'm/d/y';
18634 this.isInline = false;
18635 this.isInput = true;
18636 this.component = this.el.select('.add-on', true).first() || false;
18637 this.component = (this.component && this.component.length === 0) ? false : this.component;
18638 this.hasInput = this.component && this.inputEl().length;
18640 if (typeof(this.minViewMode === 'string')) {
18641 switch (this.minViewMode) {
18643 this.minViewMode = 1;
18646 this.minViewMode = 2;
18649 this.minViewMode = 0;
18654 if (typeof(this.viewMode === 'string')) {
18655 switch (this.viewMode) {
18668 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18670 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18672 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18674 this.picker().on('mousedown', this.onMousedown, this);
18675 this.picker().on('click', this.onClick, this);
18677 this.picker().addClass('datepicker-dropdown');
18679 this.startViewMode = this.viewMode;
18681 if(this.singleMode){
18682 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18683 v.setVisibilityMode(Roo.Element.DISPLAY);
18687 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18688 v.setStyle('width', '189px');
18692 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18693 if(!this.calendarWeeks){
18698 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18699 v.attr('colspan', function(i, val){
18700 return parseInt(val) + 1;
18705 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18707 this.setStartDate(this.startDate);
18708 this.setEndDate(this.endDate);
18710 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18717 if(this.isInline) {
18722 picker : function()
18724 return this.pickerEl;
18725 // return this.el.select('.datepicker', true).first();
18728 fillDow: function()
18730 var dowCnt = this.weekStart;
18739 if(this.calendarWeeks){
18747 while (dowCnt < this.weekStart + 7) {
18751 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18755 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18758 fillMonths: function()
18761 var months = this.picker().select('>.datepicker-months td', true).first();
18763 months.dom.innerHTML = '';
18769 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18772 months.createChild(month);
18779 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;
18781 if (this.date < this.startDate) {
18782 this.viewDate = new Date(this.startDate);
18783 } else if (this.date > this.endDate) {
18784 this.viewDate = new Date(this.endDate);
18786 this.viewDate = new Date(this.date);
18794 var d = new Date(this.viewDate),
18795 year = d.getUTCFullYear(),
18796 month = d.getUTCMonth(),
18797 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18798 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18799 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18800 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18801 currentDate = this.date && this.date.valueOf(),
18802 today = this.UTCToday();
18804 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18806 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18808 // this.picker.select('>tfoot th.today').
18809 // .text(dates[this.language].today)
18810 // .toggle(this.todayBtn !== false);
18812 this.updateNavArrows();
18815 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18817 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18819 prevMonth.setUTCDate(day);
18821 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18823 var nextMonth = new Date(prevMonth);
18825 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18827 nextMonth = nextMonth.valueOf();
18829 var fillMonths = false;
18831 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18833 while(prevMonth.valueOf() <= nextMonth) {
18836 if (prevMonth.getUTCDay() === this.weekStart) {
18838 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18846 if(this.calendarWeeks){
18847 // ISO 8601: First week contains first thursday.
18848 // ISO also states week starts on Monday, but we can be more abstract here.
18850 // Start of current week: based on weekstart/current date
18851 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18852 // Thursday of this week
18853 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18854 // First Thursday of year, year from thursday
18855 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18856 // Calendar week: ms between thursdays, div ms per day, div 7 days
18857 calWeek = (th - yth) / 864e5 / 7 + 1;
18859 fillMonths.cn.push({
18867 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18869 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18872 if (this.todayHighlight &&
18873 prevMonth.getUTCFullYear() == today.getFullYear() &&
18874 prevMonth.getUTCMonth() == today.getMonth() &&
18875 prevMonth.getUTCDate() == today.getDate()) {
18876 clsName += ' today';
18879 if (currentDate && prevMonth.valueOf() === currentDate) {
18880 clsName += ' active';
18883 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18884 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18885 clsName += ' disabled';
18888 fillMonths.cn.push({
18890 cls: 'day ' + clsName,
18891 html: prevMonth.getDate()
18894 prevMonth.setDate(prevMonth.getDate()+1);
18897 var currentYear = this.date && this.date.getUTCFullYear();
18898 var currentMonth = this.date && this.date.getUTCMonth();
18900 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18902 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18903 v.removeClass('active');
18905 if(currentYear === year && k === currentMonth){
18906 v.addClass('active');
18909 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18910 v.addClass('disabled');
18916 year = parseInt(year/10, 10) * 10;
18918 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18920 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18923 for (var i = -1; i < 11; i++) {
18924 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18926 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18934 showMode: function(dir)
18937 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18940 Roo.each(this.picker().select('>div',true).elements, function(v){
18941 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18944 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18949 if(this.isInline) {
18953 this.picker().removeClass(['bottom', 'top']);
18955 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18957 * place to the top of element!
18961 this.picker().addClass('top');
18962 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18967 this.picker().addClass('bottom');
18969 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18972 parseDate : function(value)
18974 if(!value || value instanceof Date){
18977 var v = Date.parseDate(value, this.format);
18978 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18979 v = Date.parseDate(value, 'Y-m-d');
18981 if(!v && this.altFormats){
18982 if(!this.altFormatsArray){
18983 this.altFormatsArray = this.altFormats.split("|");
18985 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18986 v = Date.parseDate(value, this.altFormatsArray[i]);
18992 formatDate : function(date, fmt)
18994 return (!date || !(date instanceof Date)) ?
18995 date : date.dateFormat(fmt || this.format);
18998 onFocus : function()
19000 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19004 onBlur : function()
19006 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19008 var d = this.inputEl().getValue();
19015 showPopup : function()
19017 this.picker().show();
19021 this.fireEvent('showpopup', this, this.date);
19024 hidePopup : function()
19026 if(this.isInline) {
19029 this.picker().hide();
19030 this.viewMode = this.startViewMode;
19033 this.fireEvent('hidepopup', this, this.date);
19037 onMousedown: function(e)
19039 e.stopPropagation();
19040 e.preventDefault();
19045 Roo.bootstrap.DateField.superclass.keyup.call(this);
19049 setValue: function(v)
19051 if(this.fireEvent('beforeselect', this, v) !== false){
19052 var d = new Date(this.parseDate(v) ).clearTime();
19054 if(isNaN(d.getTime())){
19055 this.date = this.viewDate = '';
19056 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19060 v = this.formatDate(d);
19062 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19064 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19068 this.fireEvent('select', this, this.date);
19072 getValue: function()
19074 return this.formatDate(this.date);
19077 fireKey: function(e)
19079 if (!this.picker().isVisible()){
19080 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19086 var dateChanged = false,
19088 newDate, newViewDate;
19093 e.preventDefault();
19097 if (!this.keyboardNavigation) {
19100 dir = e.keyCode == 37 ? -1 : 1;
19103 newDate = this.moveYear(this.date, dir);
19104 newViewDate = this.moveYear(this.viewDate, dir);
19105 } else if (e.shiftKey){
19106 newDate = this.moveMonth(this.date, dir);
19107 newViewDate = this.moveMonth(this.viewDate, dir);
19109 newDate = new Date(this.date);
19110 newDate.setUTCDate(this.date.getUTCDate() + dir);
19111 newViewDate = new Date(this.viewDate);
19112 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19114 if (this.dateWithinRange(newDate)){
19115 this.date = newDate;
19116 this.viewDate = newViewDate;
19117 this.setValue(this.formatDate(this.date));
19119 e.preventDefault();
19120 dateChanged = true;
19125 if (!this.keyboardNavigation) {
19128 dir = e.keyCode == 38 ? -1 : 1;
19130 newDate = this.moveYear(this.date, dir);
19131 newViewDate = this.moveYear(this.viewDate, dir);
19132 } else if (e.shiftKey){
19133 newDate = this.moveMonth(this.date, dir);
19134 newViewDate = this.moveMonth(this.viewDate, dir);
19136 newDate = new Date(this.date);
19137 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19138 newViewDate = new Date(this.viewDate);
19139 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19141 if (this.dateWithinRange(newDate)){
19142 this.date = newDate;
19143 this.viewDate = newViewDate;
19144 this.setValue(this.formatDate(this.date));
19146 e.preventDefault();
19147 dateChanged = true;
19151 this.setValue(this.formatDate(this.date));
19153 e.preventDefault();
19156 this.setValue(this.formatDate(this.date));
19170 onClick: function(e)
19172 e.stopPropagation();
19173 e.preventDefault();
19175 var target = e.getTarget();
19177 if(target.nodeName.toLowerCase() === 'i'){
19178 target = Roo.get(target).dom.parentNode;
19181 var nodeName = target.nodeName;
19182 var className = target.className;
19183 var html = target.innerHTML;
19184 //Roo.log(nodeName);
19186 switch(nodeName.toLowerCase()) {
19188 switch(className) {
19194 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19195 switch(this.viewMode){
19197 this.viewDate = this.moveMonth(this.viewDate, dir);
19201 this.viewDate = this.moveYear(this.viewDate, dir);
19207 var date = new Date();
19208 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19210 this.setValue(this.formatDate(this.date));
19217 if (className.indexOf('disabled') < 0) {
19218 this.viewDate.setUTCDate(1);
19219 if (className.indexOf('month') > -1) {
19220 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19222 var year = parseInt(html, 10) || 0;
19223 this.viewDate.setUTCFullYear(year);
19227 if(this.singleMode){
19228 this.setValue(this.formatDate(this.viewDate));
19239 //Roo.log(className);
19240 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19241 var day = parseInt(html, 10) || 1;
19242 var year = this.viewDate.getUTCFullYear(),
19243 month = this.viewDate.getUTCMonth();
19245 if (className.indexOf('old') > -1) {
19252 } else if (className.indexOf('new') > -1) {
19260 //Roo.log([year,month,day]);
19261 this.date = this.UTCDate(year, month, day,0,0,0,0);
19262 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19264 //Roo.log(this.formatDate(this.date));
19265 this.setValue(this.formatDate(this.date));
19272 setStartDate: function(startDate)
19274 this.startDate = startDate || -Infinity;
19275 if (this.startDate !== -Infinity) {
19276 this.startDate = this.parseDate(this.startDate);
19279 this.updateNavArrows();
19282 setEndDate: function(endDate)
19284 this.endDate = endDate || Infinity;
19285 if (this.endDate !== Infinity) {
19286 this.endDate = this.parseDate(this.endDate);
19289 this.updateNavArrows();
19292 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19294 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19295 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19296 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19298 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19299 return parseInt(d, 10);
19302 this.updateNavArrows();
19305 updateNavArrows: function()
19307 if(this.singleMode){
19311 var d = new Date(this.viewDate),
19312 year = d.getUTCFullYear(),
19313 month = d.getUTCMonth();
19315 Roo.each(this.picker().select('.prev', true).elements, function(v){
19317 switch (this.viewMode) {
19320 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19326 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19333 Roo.each(this.picker().select('.next', true).elements, function(v){
19335 switch (this.viewMode) {
19338 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19344 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19352 moveMonth: function(date, dir)
19357 var new_date = new Date(date.valueOf()),
19358 day = new_date.getUTCDate(),
19359 month = new_date.getUTCMonth(),
19360 mag = Math.abs(dir),
19362 dir = dir > 0 ? 1 : -1;
19365 // If going back one month, make sure month is not current month
19366 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19368 return new_date.getUTCMonth() == month;
19370 // If going forward one month, make sure month is as expected
19371 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19373 return new_date.getUTCMonth() != new_month;
19375 new_month = month + dir;
19376 new_date.setUTCMonth(new_month);
19377 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19378 if (new_month < 0 || new_month > 11) {
19379 new_month = (new_month + 12) % 12;
19382 // For magnitudes >1, move one month at a time...
19383 for (var i=0; i<mag; i++) {
19384 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19385 new_date = this.moveMonth(new_date, dir);
19387 // ...then reset the day, keeping it in the new month
19388 new_month = new_date.getUTCMonth();
19389 new_date.setUTCDate(day);
19391 return new_month != new_date.getUTCMonth();
19394 // Common date-resetting loop -- if date is beyond end of month, make it
19397 new_date.setUTCDate(--day);
19398 new_date.setUTCMonth(new_month);
19403 moveYear: function(date, dir)
19405 return this.moveMonth(date, dir*12);
19408 dateWithinRange: function(date)
19410 return date >= this.startDate && date <= this.endDate;
19416 this.picker().remove();
19419 validateValue : function(value)
19421 if(this.getVisibilityEl().hasClass('hidden')){
19425 if(value.length < 1) {
19426 if(this.allowBlank){
19432 if(value.length < this.minLength){
19435 if(value.length > this.maxLength){
19439 var vt = Roo.form.VTypes;
19440 if(!vt[this.vtype](value, this)){
19444 if(typeof this.validator == "function"){
19445 var msg = this.validator(value);
19451 if(this.regex && !this.regex.test(value)){
19455 if(typeof(this.parseDate(value)) == 'undefined'){
19459 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19463 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19473 this.date = this.viewDate = '';
19475 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19480 Roo.apply(Roo.bootstrap.DateField, {
19491 html: '<i class="fa fa-arrow-left"/>'
19501 html: '<i class="fa fa-arrow-right"/>'
19543 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19544 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19545 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19546 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19547 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19560 navFnc: 'FullYear',
19565 navFnc: 'FullYear',
19570 Roo.apply(Roo.bootstrap.DateField, {
19574 cls: 'datepicker dropdown-menu roo-dynamic',
19578 cls: 'datepicker-days',
19582 cls: 'table-condensed',
19584 Roo.bootstrap.DateField.head,
19588 Roo.bootstrap.DateField.footer
19595 cls: 'datepicker-months',
19599 cls: 'table-condensed',
19601 Roo.bootstrap.DateField.head,
19602 Roo.bootstrap.DateField.content,
19603 Roo.bootstrap.DateField.footer
19610 cls: 'datepicker-years',
19614 cls: 'table-condensed',
19616 Roo.bootstrap.DateField.head,
19617 Roo.bootstrap.DateField.content,
19618 Roo.bootstrap.DateField.footer
19637 * @class Roo.bootstrap.TimeField
19638 * @extends Roo.bootstrap.Input
19639 * Bootstrap DateField class
19643 * Create a new TimeField
19644 * @param {Object} config The config object
19647 Roo.bootstrap.TimeField = function(config){
19648 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19652 * Fires when this field show.
19653 * @param {Roo.bootstrap.DateField} thisthis
19654 * @param {Mixed} date The date value
19659 * Fires when this field hide.
19660 * @param {Roo.bootstrap.DateField} this
19661 * @param {Mixed} date The date value
19666 * Fires when select a date.
19667 * @param {Roo.bootstrap.DateField} this
19668 * @param {Mixed} date The date value
19674 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19677 * @cfg {String} format
19678 * The default time format string which can be overriden for localization support. The format must be
19679 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19683 onRender: function(ct, position)
19686 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19688 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19690 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19692 this.pop = this.picker().select('>.datepicker-time',true).first();
19693 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19695 this.picker().on('mousedown', this.onMousedown, this);
19696 this.picker().on('click', this.onClick, this);
19698 this.picker().addClass('datepicker-dropdown');
19703 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19704 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19705 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19706 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19707 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19708 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19712 fireKey: function(e){
19713 if (!this.picker().isVisible()){
19714 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19720 e.preventDefault();
19728 this.onTogglePeriod();
19731 this.onIncrementMinutes();
19734 this.onDecrementMinutes();
19743 onClick: function(e) {
19744 e.stopPropagation();
19745 e.preventDefault();
19748 picker : function()
19750 return this.el.select('.datepicker', true).first();
19753 fillTime: function()
19755 var time = this.pop.select('tbody', true).first();
19757 time.dom.innerHTML = '';
19772 cls: 'hours-up glyphicon glyphicon-chevron-up'
19792 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19813 cls: 'timepicker-hour',
19828 cls: 'timepicker-minute',
19843 cls: 'btn btn-primary period',
19865 cls: 'hours-down glyphicon glyphicon-chevron-down'
19885 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19903 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19910 var hours = this.time.getHours();
19911 var minutes = this.time.getMinutes();
19924 hours = hours - 12;
19928 hours = '0' + hours;
19932 minutes = '0' + minutes;
19935 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19936 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19937 this.pop.select('button', true).first().dom.innerHTML = period;
19943 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19945 var cls = ['bottom'];
19947 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19954 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19959 this.picker().addClass(cls.join('-'));
19963 Roo.each(cls, function(c){
19965 _this.picker().setTop(_this.inputEl().getHeight());
19969 _this.picker().setTop(0 - _this.picker().getHeight());
19974 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19978 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19985 onFocus : function()
19987 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19991 onBlur : function()
19993 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19999 this.picker().show();
20004 this.fireEvent('show', this, this.date);
20009 this.picker().hide();
20012 this.fireEvent('hide', this, this.date);
20015 setTime : function()
20018 this.setValue(this.time.format(this.format));
20020 this.fireEvent('select', this, this.date);
20025 onMousedown: function(e){
20026 e.stopPropagation();
20027 e.preventDefault();
20030 onIncrementHours: function()
20032 Roo.log('onIncrementHours');
20033 this.time = this.time.add(Date.HOUR, 1);
20038 onDecrementHours: function()
20040 Roo.log('onDecrementHours');
20041 this.time = this.time.add(Date.HOUR, -1);
20045 onIncrementMinutes: function()
20047 Roo.log('onIncrementMinutes');
20048 this.time = this.time.add(Date.MINUTE, 1);
20052 onDecrementMinutes: function()
20054 Roo.log('onDecrementMinutes');
20055 this.time = this.time.add(Date.MINUTE, -1);
20059 onTogglePeriod: function()
20061 Roo.log('onTogglePeriod');
20062 this.time = this.time.add(Date.HOUR, 12);
20069 Roo.apply(Roo.bootstrap.TimeField, {
20099 cls: 'btn btn-info ok',
20111 Roo.apply(Roo.bootstrap.TimeField, {
20115 cls: 'datepicker dropdown-menu',
20119 cls: 'datepicker-time',
20123 cls: 'table-condensed',
20125 Roo.bootstrap.TimeField.content,
20126 Roo.bootstrap.TimeField.footer
20145 * @class Roo.bootstrap.MonthField
20146 * @extends Roo.bootstrap.Input
20147 * Bootstrap MonthField class
20149 * @cfg {String} language default en
20152 * Create a new MonthField
20153 * @param {Object} config The config object
20156 Roo.bootstrap.MonthField = function(config){
20157 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20162 * Fires when this field show.
20163 * @param {Roo.bootstrap.MonthField} this
20164 * @param {Mixed} date The date value
20169 * Fires when this field hide.
20170 * @param {Roo.bootstrap.MonthField} this
20171 * @param {Mixed} date The date value
20176 * Fires when select a date.
20177 * @param {Roo.bootstrap.MonthField} this
20178 * @param {String} oldvalue The old value
20179 * @param {String} newvalue The new value
20185 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20187 onRender: function(ct, position)
20190 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20192 this.language = this.language || 'en';
20193 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20194 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20196 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20197 this.isInline = false;
20198 this.isInput = true;
20199 this.component = this.el.select('.add-on', true).first() || false;
20200 this.component = (this.component && this.component.length === 0) ? false : this.component;
20201 this.hasInput = this.component && this.inputEL().length;
20203 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20205 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20207 this.picker().on('mousedown', this.onMousedown, this);
20208 this.picker().on('click', this.onClick, this);
20210 this.picker().addClass('datepicker-dropdown');
20212 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20213 v.setStyle('width', '189px');
20220 if(this.isInline) {
20226 setValue: function(v, suppressEvent)
20228 var o = this.getValue();
20230 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20234 if(suppressEvent !== true){
20235 this.fireEvent('select', this, o, v);
20240 getValue: function()
20245 onClick: function(e)
20247 e.stopPropagation();
20248 e.preventDefault();
20250 var target = e.getTarget();
20252 if(target.nodeName.toLowerCase() === 'i'){
20253 target = Roo.get(target).dom.parentNode;
20256 var nodeName = target.nodeName;
20257 var className = target.className;
20258 var html = target.innerHTML;
20260 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20264 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20266 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20272 picker : function()
20274 return this.pickerEl;
20277 fillMonths: function()
20280 var months = this.picker().select('>.datepicker-months td', true).first();
20282 months.dom.innerHTML = '';
20288 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20291 months.createChild(month);
20300 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20301 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20304 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20305 e.removeClass('active');
20307 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20308 e.addClass('active');
20315 if(this.isInline) {
20319 this.picker().removeClass(['bottom', 'top']);
20321 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20323 * place to the top of element!
20327 this.picker().addClass('top');
20328 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20333 this.picker().addClass('bottom');
20335 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20338 onFocus : function()
20340 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20344 onBlur : function()
20346 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20348 var d = this.inputEl().getValue();
20357 this.picker().show();
20358 this.picker().select('>.datepicker-months', true).first().show();
20362 this.fireEvent('show', this, this.date);
20367 if(this.isInline) {
20370 this.picker().hide();
20371 this.fireEvent('hide', this, this.date);
20375 onMousedown: function(e)
20377 e.stopPropagation();
20378 e.preventDefault();
20383 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20387 fireKey: function(e)
20389 if (!this.picker().isVisible()){
20390 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20401 e.preventDefault();
20405 dir = e.keyCode == 37 ? -1 : 1;
20407 this.vIndex = this.vIndex + dir;
20409 if(this.vIndex < 0){
20413 if(this.vIndex > 11){
20417 if(isNaN(this.vIndex)){
20421 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20427 dir = e.keyCode == 38 ? -1 : 1;
20429 this.vIndex = this.vIndex + dir * 4;
20431 if(this.vIndex < 0){
20435 if(this.vIndex > 11){
20439 if(isNaN(this.vIndex)){
20443 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20448 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20449 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20453 e.preventDefault();
20456 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20457 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20473 this.picker().remove();
20478 Roo.apply(Roo.bootstrap.MonthField, {
20497 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20498 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20503 Roo.apply(Roo.bootstrap.MonthField, {
20507 cls: 'datepicker dropdown-menu roo-dynamic',
20511 cls: 'datepicker-months',
20515 cls: 'table-condensed',
20517 Roo.bootstrap.DateField.content
20537 * @class Roo.bootstrap.CheckBox
20538 * @extends Roo.bootstrap.Input
20539 * Bootstrap CheckBox class
20541 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20542 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20543 * @cfg {String} boxLabel The text that appears beside the checkbox
20544 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20545 * @cfg {Boolean} checked initnal the element
20546 * @cfg {Boolean} inline inline the element (default false)
20547 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20548 * @cfg {String} tooltip label tooltip
20551 * Create a new CheckBox
20552 * @param {Object} config The config object
20555 Roo.bootstrap.CheckBox = function(config){
20556 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20561 * Fires when the element is checked or unchecked.
20562 * @param {Roo.bootstrap.CheckBox} this This input
20563 * @param {Boolean} checked The new checked value
20568 * Fires when the element is click.
20569 * @param {Roo.bootstrap.CheckBox} this This input
20576 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20578 inputType: 'checkbox',
20587 getAutoCreate : function()
20589 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20595 cfg.cls = 'form-group ' + this.inputType; //input-group
20598 cfg.cls += ' ' + this.inputType + '-inline';
20604 type : this.inputType,
20605 value : this.inputValue,
20606 cls : 'roo-' + this.inputType, //'form-box',
20607 placeholder : this.placeholder || ''
20611 if(this.inputType != 'radio'){
20615 cls : 'roo-hidden-value',
20616 value : this.checked ? this.inputValue : this.valueOff
20621 if (this.weight) { // Validity check?
20622 cfg.cls += " " + this.inputType + "-" + this.weight;
20625 if (this.disabled) {
20626 input.disabled=true;
20630 input.checked = this.checked;
20635 input.name = this.name;
20637 if(this.inputType != 'radio'){
20638 hidden.name = this.name;
20639 input.name = '_hidden_' + this.name;
20644 input.cls += ' input-' + this.size;
20649 ['xs','sm','md','lg'].map(function(size){
20650 if (settings[size]) {
20651 cfg.cls += ' col-' + size + '-' + settings[size];
20655 var inputblock = input;
20657 if (this.before || this.after) {
20660 cls : 'input-group',
20665 inputblock.cn.push({
20667 cls : 'input-group-addon',
20672 inputblock.cn.push(input);
20674 if(this.inputType != 'radio'){
20675 inputblock.cn.push(hidden);
20679 inputblock.cn.push({
20681 cls : 'input-group-addon',
20688 if (align ==='left' && this.fieldLabel.length) {
20689 // Roo.log("left and has label");
20694 cls : 'control-label',
20695 html : this.fieldLabel
20705 if(this.labelWidth > 12){
20706 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20709 if(this.labelWidth < 13 && this.labelmd == 0){
20710 this.labelmd = this.labelWidth;
20713 if(this.labellg > 0){
20714 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20715 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20718 if(this.labelmd > 0){
20719 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20720 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20723 if(this.labelsm > 0){
20724 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20725 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20728 if(this.labelxs > 0){
20729 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20730 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20733 } else if ( this.fieldLabel.length) {
20734 // Roo.log(" label");
20738 tag: this.boxLabel ? 'span' : 'label',
20740 cls: 'control-label box-input-label',
20741 //cls : 'input-group-addon',
20742 html : this.fieldLabel
20751 // Roo.log(" no label && no align");
20752 cfg.cn = [ inputblock ] ;
20758 var boxLabelCfg = {
20760 //'for': id, // box label is handled by onclick - so no for...
20762 html: this.boxLabel
20766 boxLabelCfg.tooltip = this.tooltip;
20769 cfg.cn.push(boxLabelCfg);
20772 if(this.inputType != 'radio'){
20773 cfg.cn.push(hidden);
20781 * return the real input element.
20783 inputEl: function ()
20785 return this.el.select('input.roo-' + this.inputType,true).first();
20787 hiddenEl: function ()
20789 return this.el.select('input.roo-hidden-value',true).first();
20792 labelEl: function()
20794 return this.el.select('label.control-label',true).first();
20796 /* depricated... */
20800 return this.labelEl();
20803 boxLabelEl: function()
20805 return this.el.select('label.box-label',true).first();
20808 initEvents : function()
20810 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20812 this.inputEl().on('click', this.onClick, this);
20814 if (this.boxLabel) {
20815 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20818 this.startValue = this.getValue();
20821 Roo.bootstrap.CheckBox.register(this);
20825 onClick : function(e)
20827 if(this.fireEvent('click', this, e) !== false){
20828 this.setChecked(!this.checked);
20833 setChecked : function(state,suppressEvent)
20835 this.startValue = this.getValue();
20837 if(this.inputType == 'radio'){
20839 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20840 e.dom.checked = false;
20843 this.inputEl().dom.checked = true;
20845 this.inputEl().dom.value = this.inputValue;
20847 if(suppressEvent !== true){
20848 this.fireEvent('check', this, true);
20856 this.checked = state;
20858 this.inputEl().dom.checked = state;
20861 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20863 if(suppressEvent !== true){
20864 this.fireEvent('check', this, state);
20870 getValue : function()
20872 if(this.inputType == 'radio'){
20873 return this.getGroupValue();
20876 return this.hiddenEl().dom.value;
20880 getGroupValue : function()
20882 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20886 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20889 setValue : function(v,suppressEvent)
20891 if(this.inputType == 'radio'){
20892 this.setGroupValue(v, suppressEvent);
20896 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20901 setGroupValue : function(v, suppressEvent)
20903 this.startValue = this.getValue();
20905 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20906 e.dom.checked = false;
20908 if(e.dom.value == v){
20909 e.dom.checked = true;
20913 if(suppressEvent !== true){
20914 this.fireEvent('check', this, true);
20922 validate : function()
20924 if(this.getVisibilityEl().hasClass('hidden')){
20930 (this.inputType == 'radio' && this.validateRadio()) ||
20931 (this.inputType == 'checkbox' && this.validateCheckbox())
20937 this.markInvalid();
20941 validateRadio : function()
20943 if(this.getVisibilityEl().hasClass('hidden')){
20947 if(this.allowBlank){
20953 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20954 if(!e.dom.checked){
20966 validateCheckbox : function()
20969 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20970 //return (this.getValue() == this.inputValue) ? true : false;
20973 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20981 for(var i in group){
20982 if(group[i].el.isVisible(true)){
20990 for(var i in group){
20995 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21002 * Mark this field as valid
21004 markValid : function()
21008 this.fireEvent('valid', this);
21010 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21013 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21020 if(this.inputType == 'radio'){
21021 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21022 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21023 e.findParent('.form-group', false, true).addClass(_this.validClass);
21030 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21031 this.el.findParent('.form-group', false, true).addClass(this.validClass);
21035 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21041 for(var i in group){
21042 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21043 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21048 * Mark this field as invalid
21049 * @param {String} msg The validation message
21051 markInvalid : function(msg)
21053 if(this.allowBlank){
21059 this.fireEvent('invalid', this, msg);
21061 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21064 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21068 label.markInvalid();
21071 if(this.inputType == 'radio'){
21072 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21073 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21074 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21081 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21082 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21086 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21092 for(var i in group){
21093 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21094 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21099 clearInvalid : function()
21101 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21103 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21105 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21107 if (label && label.iconEl) {
21108 label.iconEl.removeClass(label.validClass);
21109 label.iconEl.removeClass(label.invalidClass);
21113 disable : function()
21115 if(this.inputType != 'radio'){
21116 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21123 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21124 _this.getActionEl().addClass(this.disabledClass);
21125 e.dom.disabled = true;
21129 this.disabled = true;
21130 this.fireEvent("disable", this);
21134 enable : function()
21136 if(this.inputType != 'radio'){
21137 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21144 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21145 _this.getActionEl().removeClass(this.disabledClass);
21146 e.dom.disabled = false;
21150 this.disabled = false;
21151 this.fireEvent("enable", this);
21155 setBoxLabel : function(v)
21160 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21166 Roo.apply(Roo.bootstrap.CheckBox, {
21171 * register a CheckBox Group
21172 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21174 register : function(checkbox)
21176 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21177 this.groups[checkbox.groupId] = {};
21180 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21184 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21188 * fetch a CheckBox Group based on the group ID
21189 * @param {string} the group ID
21190 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21192 get: function(groupId) {
21193 if (typeof(this.groups[groupId]) == 'undefined') {
21197 return this.groups[groupId] ;
21210 * @class Roo.bootstrap.Radio
21211 * @extends Roo.bootstrap.Component
21212 * Bootstrap Radio class
21213 * @cfg {String} boxLabel - the label associated
21214 * @cfg {String} value - the value of radio
21217 * Create a new Radio
21218 * @param {Object} config The config object
21220 Roo.bootstrap.Radio = function(config){
21221 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21225 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21231 getAutoCreate : function()
21235 cls : 'form-group radio',
21240 html : this.boxLabel
21248 initEvents : function()
21250 this.parent().register(this);
21252 this.el.on('click', this.onClick, this);
21256 onClick : function(e)
21258 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21259 this.setChecked(true);
21263 setChecked : function(state, suppressEvent)
21265 this.parent().setValue(this.value, suppressEvent);
21269 setBoxLabel : function(v)
21274 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21289 * @class Roo.bootstrap.SecurePass
21290 * @extends Roo.bootstrap.Input
21291 * Bootstrap SecurePass class
21295 * Create a new SecurePass
21296 * @param {Object} config The config object
21299 Roo.bootstrap.SecurePass = function (config) {
21300 // these go here, so the translation tool can replace them..
21302 PwdEmpty: "Please type a password, and then retype it to confirm.",
21303 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21304 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21305 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21306 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21307 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21308 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21309 TooWeak: "Your password is Too Weak."
21311 this.meterLabel = "Password strength:";
21312 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21313 this.meterClass = [
21314 "roo-password-meter-tooweak",
21315 "roo-password-meter-weak",
21316 "roo-password-meter-medium",
21317 "roo-password-meter-strong",
21318 "roo-password-meter-grey"
21323 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21326 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21328 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21330 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21331 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21332 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21333 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21334 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21335 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21336 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21346 * @cfg {String/Object} Label for the strength meter (defaults to
21347 * 'Password strength:')
21352 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21353 * ['Weak', 'Medium', 'Strong'])
21356 pwdStrengths: false,
21369 initEvents: function ()
21371 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21373 if (this.el.is('input[type=password]') && Roo.isSafari) {
21374 this.el.on('keydown', this.SafariOnKeyDown, this);
21377 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21380 onRender: function (ct, position)
21382 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21383 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21384 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21386 this.trigger.createChild({
21391 cls: 'roo-password-meter-grey col-xs-12',
21394 //width: this.meterWidth + 'px'
21398 cls: 'roo-password-meter-text'
21404 if (this.hideTrigger) {
21405 this.trigger.setDisplayed(false);
21407 this.setSize(this.width || '', this.height || '');
21410 onDestroy: function ()
21412 if (this.trigger) {
21413 this.trigger.removeAllListeners();
21414 this.trigger.remove();
21417 this.wrap.remove();
21419 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21422 checkStrength: function ()
21424 var pwd = this.inputEl().getValue();
21425 if (pwd == this._lastPwd) {
21430 if (this.ClientSideStrongPassword(pwd)) {
21432 } else if (this.ClientSideMediumPassword(pwd)) {
21434 } else if (this.ClientSideWeakPassword(pwd)) {
21440 Roo.log('strength1: ' + strength);
21442 //var pm = this.trigger.child('div/div/div').dom;
21443 var pm = this.trigger.child('div/div');
21444 pm.removeClass(this.meterClass);
21445 pm.addClass(this.meterClass[strength]);
21448 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21450 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21452 this._lastPwd = pwd;
21456 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21458 this._lastPwd = '';
21460 var pm = this.trigger.child('div/div');
21461 pm.removeClass(this.meterClass);
21462 pm.addClass('roo-password-meter-grey');
21465 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21468 this.inputEl().dom.type='password';
21471 validateValue: function (value)
21474 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21477 if (value.length == 0) {
21478 if (this.allowBlank) {
21479 this.clearInvalid();
21483 this.markInvalid(this.errors.PwdEmpty);
21484 this.errorMsg = this.errors.PwdEmpty;
21492 if ('[\x21-\x7e]*'.match(value)) {
21493 this.markInvalid(this.errors.PwdBadChar);
21494 this.errorMsg = this.errors.PwdBadChar;
21497 if (value.length < 6) {
21498 this.markInvalid(this.errors.PwdShort);
21499 this.errorMsg = this.errors.PwdShort;
21502 if (value.length > 16) {
21503 this.markInvalid(this.errors.PwdLong);
21504 this.errorMsg = this.errors.PwdLong;
21508 if (this.ClientSideStrongPassword(value)) {
21510 } else if (this.ClientSideMediumPassword(value)) {
21512 } else if (this.ClientSideWeakPassword(value)) {
21519 if (strength < 2) {
21520 //this.markInvalid(this.errors.TooWeak);
21521 this.errorMsg = this.errors.TooWeak;
21526 console.log('strength2: ' + strength);
21528 //var pm = this.trigger.child('div/div/div').dom;
21530 var pm = this.trigger.child('div/div');
21531 pm.removeClass(this.meterClass);
21532 pm.addClass(this.meterClass[strength]);
21534 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21536 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21538 this.errorMsg = '';
21542 CharacterSetChecks: function (type)
21545 this.fResult = false;
21548 isctype: function (character, type)
21551 case this.kCapitalLetter:
21552 if (character >= 'A' && character <= 'Z') {
21557 case this.kSmallLetter:
21558 if (character >= 'a' && character <= 'z') {
21564 if (character >= '0' && character <= '9') {
21569 case this.kPunctuation:
21570 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21581 IsLongEnough: function (pwd, size)
21583 return !(pwd == null || isNaN(size) || pwd.length < size);
21586 SpansEnoughCharacterSets: function (word, nb)
21588 if (!this.IsLongEnough(word, nb))
21593 var characterSetChecks = new Array(
21594 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21595 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21598 for (var index = 0; index < word.length; ++index) {
21599 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21600 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21601 characterSetChecks[nCharSet].fResult = true;
21608 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21609 if (characterSetChecks[nCharSet].fResult) {
21614 if (nCharSets < nb) {
21620 ClientSideStrongPassword: function (pwd)
21622 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21625 ClientSideMediumPassword: function (pwd)
21627 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21630 ClientSideWeakPassword: function (pwd)
21632 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21635 })//<script type="text/javascript">
21638 * Based Ext JS Library 1.1.1
21639 * Copyright(c) 2006-2007, Ext JS, LLC.
21645 * @class Roo.HtmlEditorCore
21646 * @extends Roo.Component
21647 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21649 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21652 Roo.HtmlEditorCore = function(config){
21655 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21660 * @event initialize
21661 * Fires when the editor is fully initialized (including the iframe)
21662 * @param {Roo.HtmlEditorCore} this
21667 * Fires when the editor is first receives the focus. Any insertion must wait
21668 * until after this event.
21669 * @param {Roo.HtmlEditorCore} this
21673 * @event beforesync
21674 * Fires before the textarea is updated with content from the editor iframe. Return false
21675 * to cancel the sync.
21676 * @param {Roo.HtmlEditorCore} this
21677 * @param {String} html
21681 * @event beforepush
21682 * Fires before the iframe editor is updated with content from the textarea. Return false
21683 * to cancel the push.
21684 * @param {Roo.HtmlEditorCore} this
21685 * @param {String} html
21690 * Fires when the textarea is updated with content from the editor iframe.
21691 * @param {Roo.HtmlEditorCore} this
21692 * @param {String} html
21697 * Fires when the iframe editor is updated with content from the textarea.
21698 * @param {Roo.HtmlEditorCore} this
21699 * @param {String} html
21704 * @event editorevent
21705 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21706 * @param {Roo.HtmlEditorCore} this
21712 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21714 // defaults : white / black...
21715 this.applyBlacklists();
21722 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21726 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21732 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21737 * @cfg {Number} height (in pixels)
21741 * @cfg {Number} width (in pixels)
21746 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21749 stylesheets: false,
21754 // private properties
21755 validationEvent : false,
21757 initialized : false,
21759 sourceEditMode : false,
21760 onFocus : Roo.emptyFn,
21762 hideMode:'offsets',
21766 // blacklist + whitelisted elements..
21773 * Protected method that will not generally be called directly. It
21774 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21775 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21777 getDocMarkup : function(){
21781 // inherit styels from page...??
21782 if (this.stylesheets === false) {
21784 Roo.get(document.head).select('style').each(function(node) {
21785 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21788 Roo.get(document.head).select('link').each(function(node) {
21789 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21792 } else if (!this.stylesheets.length) {
21794 st = '<style type="text/css">' +
21795 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21798 st = '<style type="text/css">' +
21803 st += '<style type="text/css">' +
21804 'IMG { cursor: pointer } ' +
21807 var cls = 'roo-htmleditor-body';
21809 if(this.bodyCls.length){
21810 cls += ' ' + this.bodyCls;
21813 return '<html><head>' + st +
21814 //<style type="text/css">' +
21815 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21817 ' </head><body class="' + cls + '"></body></html>';
21821 onRender : function(ct, position)
21824 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21825 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21828 this.el.dom.style.border = '0 none';
21829 this.el.dom.setAttribute('tabIndex', -1);
21830 this.el.addClass('x-hidden hide');
21834 if(Roo.isIE){ // fix IE 1px bogus margin
21835 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21839 this.frameId = Roo.id();
21843 var iframe = this.owner.wrap.createChild({
21845 cls: 'form-control', // bootstrap..
21847 name: this.frameId,
21848 frameBorder : 'no',
21849 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21854 this.iframe = iframe.dom;
21856 this.assignDocWin();
21858 this.doc.designMode = 'on';
21861 this.doc.write(this.getDocMarkup());
21865 var task = { // must defer to wait for browser to be ready
21867 //console.log("run task?" + this.doc.readyState);
21868 this.assignDocWin();
21869 if(this.doc.body || this.doc.readyState == 'complete'){
21871 this.doc.designMode="on";
21875 Roo.TaskMgr.stop(task);
21876 this.initEditor.defer(10, this);
21883 Roo.TaskMgr.start(task);
21888 onResize : function(w, h)
21890 Roo.log('resize: ' +w + ',' + h );
21891 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21895 if(typeof w == 'number'){
21897 this.iframe.style.width = w + 'px';
21899 if(typeof h == 'number'){
21901 this.iframe.style.height = h + 'px';
21903 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21910 * Toggles the editor between standard and source edit mode.
21911 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21913 toggleSourceEdit : function(sourceEditMode){
21915 this.sourceEditMode = sourceEditMode === true;
21917 if(this.sourceEditMode){
21919 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21922 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21923 //this.iframe.className = '';
21926 //this.setSize(this.owner.wrap.getSize());
21927 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21934 * Protected method that will not generally be called directly. If you need/want
21935 * custom HTML cleanup, this is the method you should override.
21936 * @param {String} html The HTML to be cleaned
21937 * return {String} The cleaned HTML
21939 cleanHtml : function(html){
21940 html = String(html);
21941 if(html.length > 5){
21942 if(Roo.isSafari){ // strip safari nonsense
21943 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21946 if(html == ' '){
21953 * HTML Editor -> Textarea
21954 * Protected method that will not generally be called directly. Syncs the contents
21955 * of the editor iframe with the textarea.
21957 syncValue : function(){
21958 if(this.initialized){
21959 var bd = (this.doc.body || this.doc.documentElement);
21960 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21961 var html = bd.innerHTML;
21963 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21964 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21966 html = '<div style="'+m[0]+'">' + html + '</div>';
21969 html = this.cleanHtml(html);
21970 // fix up the special chars.. normaly like back quotes in word...
21971 // however we do not want to do this with chinese..
21972 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21973 var cc = b.charCodeAt();
21975 (cc >= 0x4E00 && cc < 0xA000 ) ||
21976 (cc >= 0x3400 && cc < 0x4E00 ) ||
21977 (cc >= 0xf900 && cc < 0xfb00 )
21983 if(this.owner.fireEvent('beforesync', this, html) !== false){
21984 this.el.dom.value = html;
21985 this.owner.fireEvent('sync', this, html);
21991 * Protected method that will not generally be called directly. Pushes the value of the textarea
21992 * into the iframe editor.
21994 pushValue : function(){
21995 if(this.initialized){
21996 var v = this.el.dom.value.trim();
21998 // if(v.length < 1){
22002 if(this.owner.fireEvent('beforepush', this, v) !== false){
22003 var d = (this.doc.body || this.doc.documentElement);
22005 this.cleanUpPaste();
22006 this.el.dom.value = d.innerHTML;
22007 this.owner.fireEvent('push', this, v);
22013 deferFocus : function(){
22014 this.focus.defer(10, this);
22018 focus : function(){
22019 if(this.win && !this.sourceEditMode){
22026 assignDocWin: function()
22028 var iframe = this.iframe;
22031 this.doc = iframe.contentWindow.document;
22032 this.win = iframe.contentWindow;
22034 // if (!Roo.get(this.frameId)) {
22037 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22038 // this.win = Roo.get(this.frameId).dom.contentWindow;
22040 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22044 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22045 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22050 initEditor : function(){
22051 //console.log("INIT EDITOR");
22052 this.assignDocWin();
22056 this.doc.designMode="on";
22058 this.doc.write(this.getDocMarkup());
22061 var dbody = (this.doc.body || this.doc.documentElement);
22062 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22063 // this copies styles from the containing element into thsi one..
22064 // not sure why we need all of this..
22065 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22067 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22068 //ss['background-attachment'] = 'fixed'; // w3c
22069 dbody.bgProperties = 'fixed'; // ie
22070 //Roo.DomHelper.applyStyles(dbody, ss);
22071 Roo.EventManager.on(this.doc, {
22072 //'mousedown': this.onEditorEvent,
22073 'mouseup': this.onEditorEvent,
22074 'dblclick': this.onEditorEvent,
22075 'click': this.onEditorEvent,
22076 'keyup': this.onEditorEvent,
22081 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22083 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22084 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22086 this.initialized = true;
22088 this.owner.fireEvent('initialize', this);
22093 onDestroy : function(){
22099 //for (var i =0; i < this.toolbars.length;i++) {
22100 // // fixme - ask toolbars for heights?
22101 // this.toolbars[i].onDestroy();
22104 //this.wrap.dom.innerHTML = '';
22105 //this.wrap.remove();
22110 onFirstFocus : function(){
22112 this.assignDocWin();
22115 this.activated = true;
22118 if(Roo.isGecko){ // prevent silly gecko errors
22120 var s = this.win.getSelection();
22121 if(!s.focusNode || s.focusNode.nodeType != 3){
22122 var r = s.getRangeAt(0);
22123 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22128 this.execCmd('useCSS', true);
22129 this.execCmd('styleWithCSS', false);
22132 this.owner.fireEvent('activate', this);
22136 adjustFont: function(btn){
22137 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22138 //if(Roo.isSafari){ // safari
22141 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22142 if(Roo.isSafari){ // safari
22143 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22144 v = (v < 10) ? 10 : v;
22145 v = (v > 48) ? 48 : v;
22146 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22151 v = Math.max(1, v+adjust);
22153 this.execCmd('FontSize', v );
22156 onEditorEvent : function(e)
22158 this.owner.fireEvent('editorevent', this, e);
22159 // this.updateToolbar();
22160 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22163 insertTag : function(tg)
22165 // could be a bit smarter... -> wrap the current selected tRoo..
22166 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22168 range = this.createRange(this.getSelection());
22169 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22170 wrappingNode.appendChild(range.extractContents());
22171 range.insertNode(wrappingNode);
22178 this.execCmd("formatblock", tg);
22182 insertText : function(txt)
22186 var range = this.createRange();
22187 range.deleteContents();
22188 //alert(Sender.getAttribute('label'));
22190 range.insertNode(this.doc.createTextNode(txt));
22196 * Executes a Midas editor command on the editor document and performs necessary focus and
22197 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22198 * @param {String} cmd The Midas command
22199 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22201 relayCmd : function(cmd, value){
22203 this.execCmd(cmd, value);
22204 this.owner.fireEvent('editorevent', this);
22205 //this.updateToolbar();
22206 this.owner.deferFocus();
22210 * Executes a Midas editor command directly on the editor document.
22211 * For visual commands, you should use {@link #relayCmd} instead.
22212 * <b>This should only be called after the editor is initialized.</b>
22213 * @param {String} cmd The Midas command
22214 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22216 execCmd : function(cmd, value){
22217 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22224 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22226 * @param {String} text | dom node..
22228 insertAtCursor : function(text)
22231 if(!this.activated){
22237 var r = this.doc.selection.createRange();
22248 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22252 // from jquery ui (MIT licenced)
22254 var win = this.win;
22256 if (win.getSelection && win.getSelection().getRangeAt) {
22257 range = win.getSelection().getRangeAt(0);
22258 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22259 range.insertNode(node);
22260 } else if (win.document.selection && win.document.selection.createRange) {
22261 // no firefox support
22262 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22263 win.document.selection.createRange().pasteHTML(txt);
22265 // no firefox support
22266 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22267 this.execCmd('InsertHTML', txt);
22276 mozKeyPress : function(e){
22278 var c = e.getCharCode(), cmd;
22281 c = String.fromCharCode(c).toLowerCase();
22295 this.cleanUpPaste.defer(100, this);
22303 e.preventDefault();
22311 fixKeys : function(){ // load time branching for fastest keydown performance
22313 return function(e){
22314 var k = e.getKey(), r;
22317 r = this.doc.selection.createRange();
22320 r.pasteHTML('    ');
22327 r = this.doc.selection.createRange();
22329 var target = r.parentElement();
22330 if(!target || target.tagName.toLowerCase() != 'li'){
22332 r.pasteHTML('<br />');
22338 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22339 this.cleanUpPaste.defer(100, this);
22345 }else if(Roo.isOpera){
22346 return function(e){
22347 var k = e.getKey();
22351 this.execCmd('InsertHTML','    ');
22354 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22355 this.cleanUpPaste.defer(100, this);
22360 }else if(Roo.isSafari){
22361 return function(e){
22362 var k = e.getKey();
22366 this.execCmd('InsertText','\t');
22370 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22371 this.cleanUpPaste.defer(100, this);
22379 getAllAncestors: function()
22381 var p = this.getSelectedNode();
22384 a.push(p); // push blank onto stack..
22385 p = this.getParentElement();
22389 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22393 a.push(this.doc.body);
22397 lastSelNode : false,
22400 getSelection : function()
22402 this.assignDocWin();
22403 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22406 getSelectedNode: function()
22408 // this may only work on Gecko!!!
22410 // should we cache this!!!!
22415 var range = this.createRange(this.getSelection()).cloneRange();
22418 var parent = range.parentElement();
22420 var testRange = range.duplicate();
22421 testRange.moveToElementText(parent);
22422 if (testRange.inRange(range)) {
22425 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22428 parent = parent.parentElement;
22433 // is ancestor a text element.
22434 var ac = range.commonAncestorContainer;
22435 if (ac.nodeType == 3) {
22436 ac = ac.parentNode;
22439 var ar = ac.childNodes;
22442 var other_nodes = [];
22443 var has_other_nodes = false;
22444 for (var i=0;i<ar.length;i++) {
22445 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22448 // fullly contained node.
22450 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22455 // probably selected..
22456 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22457 other_nodes.push(ar[i]);
22461 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22466 has_other_nodes = true;
22468 if (!nodes.length && other_nodes.length) {
22469 nodes= other_nodes;
22471 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22477 createRange: function(sel)
22479 // this has strange effects when using with
22480 // top toolbar - not sure if it's a great idea.
22481 //this.editor.contentWindow.focus();
22482 if (typeof sel != "undefined") {
22484 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22486 return this.doc.createRange();
22489 return this.doc.createRange();
22492 getParentElement: function()
22495 this.assignDocWin();
22496 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22498 var range = this.createRange(sel);
22501 var p = range.commonAncestorContainer;
22502 while (p.nodeType == 3) { // text node
22513 * Range intersection.. the hard stuff...
22517 * [ -- selected range --- ]
22521 * if end is before start or hits it. fail.
22522 * if start is after end or hits it fail.
22524 * if either hits (but other is outside. - then it's not
22530 // @see http://www.thismuchiknow.co.uk/?p=64.
22531 rangeIntersectsNode : function(range, node)
22533 var nodeRange = node.ownerDocument.createRange();
22535 nodeRange.selectNode(node);
22537 nodeRange.selectNodeContents(node);
22540 var rangeStartRange = range.cloneRange();
22541 rangeStartRange.collapse(true);
22543 var rangeEndRange = range.cloneRange();
22544 rangeEndRange.collapse(false);
22546 var nodeStartRange = nodeRange.cloneRange();
22547 nodeStartRange.collapse(true);
22549 var nodeEndRange = nodeRange.cloneRange();
22550 nodeEndRange.collapse(false);
22552 return rangeStartRange.compareBoundaryPoints(
22553 Range.START_TO_START, nodeEndRange) == -1 &&
22554 rangeEndRange.compareBoundaryPoints(
22555 Range.START_TO_START, nodeStartRange) == 1;
22559 rangeCompareNode : function(range, node)
22561 var nodeRange = node.ownerDocument.createRange();
22563 nodeRange.selectNode(node);
22565 nodeRange.selectNodeContents(node);
22569 range.collapse(true);
22571 nodeRange.collapse(true);
22573 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22574 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22576 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22578 var nodeIsBefore = ss == 1;
22579 var nodeIsAfter = ee == -1;
22581 if (nodeIsBefore && nodeIsAfter) {
22584 if (!nodeIsBefore && nodeIsAfter) {
22585 return 1; //right trailed.
22588 if (nodeIsBefore && !nodeIsAfter) {
22589 return 2; // left trailed.
22595 // private? - in a new class?
22596 cleanUpPaste : function()
22598 // cleans up the whole document..
22599 Roo.log('cleanuppaste');
22601 this.cleanUpChildren(this.doc.body);
22602 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22603 if (clean != this.doc.body.innerHTML) {
22604 this.doc.body.innerHTML = clean;
22609 cleanWordChars : function(input) {// change the chars to hex code
22610 var he = Roo.HtmlEditorCore;
22612 var output = input;
22613 Roo.each(he.swapCodes, function(sw) {
22614 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22616 output = output.replace(swapper, sw[1]);
22623 cleanUpChildren : function (n)
22625 if (!n.childNodes.length) {
22628 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22629 this.cleanUpChild(n.childNodes[i]);
22636 cleanUpChild : function (node)
22639 //console.log(node);
22640 if (node.nodeName == "#text") {
22641 // clean up silly Windows -- stuff?
22644 if (node.nodeName == "#comment") {
22645 node.parentNode.removeChild(node);
22646 // clean up silly Windows -- stuff?
22649 var lcname = node.tagName.toLowerCase();
22650 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22651 // whitelist of tags..
22653 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22655 node.parentNode.removeChild(node);
22660 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22662 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22663 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22665 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22666 // remove_keep_children = true;
22669 if (remove_keep_children) {
22670 this.cleanUpChildren(node);
22671 // inserts everything just before this node...
22672 while (node.childNodes.length) {
22673 var cn = node.childNodes[0];
22674 node.removeChild(cn);
22675 node.parentNode.insertBefore(cn, node);
22677 node.parentNode.removeChild(node);
22681 if (!node.attributes || !node.attributes.length) {
22682 this.cleanUpChildren(node);
22686 function cleanAttr(n,v)
22689 if (v.match(/^\./) || v.match(/^\//)) {
22692 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22695 if (v.match(/^#/)) {
22698 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22699 node.removeAttribute(n);
22703 var cwhite = this.cwhite;
22704 var cblack = this.cblack;
22706 function cleanStyle(n,v)
22708 if (v.match(/expression/)) { //XSS?? should we even bother..
22709 node.removeAttribute(n);
22713 var parts = v.split(/;/);
22716 Roo.each(parts, function(p) {
22717 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22721 var l = p.split(':').shift().replace(/\s+/g,'');
22722 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22724 if ( cwhite.length && cblack.indexOf(l) > -1) {
22725 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22726 //node.removeAttribute(n);
22730 // only allow 'c whitelisted system attributes'
22731 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22732 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22733 //node.removeAttribute(n);
22743 if (clean.length) {
22744 node.setAttribute(n, clean.join(';'));
22746 node.removeAttribute(n);
22752 for (var i = node.attributes.length-1; i > -1 ; i--) {
22753 var a = node.attributes[i];
22756 if (a.name.toLowerCase().substr(0,2)=='on') {
22757 node.removeAttribute(a.name);
22760 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22761 node.removeAttribute(a.name);
22764 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22765 cleanAttr(a.name,a.value); // fixme..
22768 if (a.name == 'style') {
22769 cleanStyle(a.name,a.value);
22772 /// clean up MS crap..
22773 // tecnically this should be a list of valid class'es..
22776 if (a.name == 'class') {
22777 if (a.value.match(/^Mso/)) {
22778 node.className = '';
22781 if (a.value.match(/^body$/)) {
22782 node.className = '';
22793 this.cleanUpChildren(node);
22799 * Clean up MS wordisms...
22801 cleanWord : function(node)
22806 this.cleanWord(this.doc.body);
22809 if (node.nodeName == "#text") {
22810 // clean up silly Windows -- stuff?
22813 if (node.nodeName == "#comment") {
22814 node.parentNode.removeChild(node);
22815 // clean up silly Windows -- stuff?
22819 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22820 node.parentNode.removeChild(node);
22824 // remove - but keep children..
22825 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22826 while (node.childNodes.length) {
22827 var cn = node.childNodes[0];
22828 node.removeChild(cn);
22829 node.parentNode.insertBefore(cn, node);
22831 node.parentNode.removeChild(node);
22832 this.iterateChildren(node, this.cleanWord);
22836 if (node.className.length) {
22838 var cn = node.className.split(/\W+/);
22840 Roo.each(cn, function(cls) {
22841 if (cls.match(/Mso[a-zA-Z]+/)) {
22846 node.className = cna.length ? cna.join(' ') : '';
22848 node.removeAttribute("class");
22852 if (node.hasAttribute("lang")) {
22853 node.removeAttribute("lang");
22856 if (node.hasAttribute("style")) {
22858 var styles = node.getAttribute("style").split(";");
22860 Roo.each(styles, function(s) {
22861 if (!s.match(/:/)) {
22864 var kv = s.split(":");
22865 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22868 // what ever is left... we allow.
22871 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22872 if (!nstyle.length) {
22873 node.removeAttribute('style');
22876 this.iterateChildren(node, this.cleanWord);
22882 * iterateChildren of a Node, calling fn each time, using this as the scole..
22883 * @param {DomNode} node node to iterate children of.
22884 * @param {Function} fn method of this class to call on each item.
22886 iterateChildren : function(node, fn)
22888 if (!node.childNodes.length) {
22891 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22892 fn.call(this, node.childNodes[i])
22898 * cleanTableWidths.
22900 * Quite often pasting from word etc.. results in tables with column and widths.
22901 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22904 cleanTableWidths : function(node)
22909 this.cleanTableWidths(this.doc.body);
22914 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22917 Roo.log(node.tagName);
22918 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22919 this.iterateChildren(node, this.cleanTableWidths);
22922 if (node.hasAttribute('width')) {
22923 node.removeAttribute('width');
22927 if (node.hasAttribute("style")) {
22930 var styles = node.getAttribute("style").split(";");
22932 Roo.each(styles, function(s) {
22933 if (!s.match(/:/)) {
22936 var kv = s.split(":");
22937 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22940 // what ever is left... we allow.
22943 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22944 if (!nstyle.length) {
22945 node.removeAttribute('style');
22949 this.iterateChildren(node, this.cleanTableWidths);
22957 domToHTML : function(currentElement, depth, nopadtext) {
22959 depth = depth || 0;
22960 nopadtext = nopadtext || false;
22962 if (!currentElement) {
22963 return this.domToHTML(this.doc.body);
22966 //Roo.log(currentElement);
22968 var allText = false;
22969 var nodeName = currentElement.nodeName;
22970 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22972 if (nodeName == '#text') {
22974 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22979 if (nodeName != 'BODY') {
22982 // Prints the node tagName, such as <A>, <IMG>, etc
22985 for(i = 0; i < currentElement.attributes.length;i++) {
22987 var aname = currentElement.attributes.item(i).name;
22988 if (!currentElement.attributes.item(i).value.length) {
22991 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22994 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23003 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23006 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23011 // Traverse the tree
23013 var currentElementChild = currentElement.childNodes.item(i);
23014 var allText = true;
23015 var innerHTML = '';
23017 while (currentElementChild) {
23018 // Formatting code (indent the tree so it looks nice on the screen)
23019 var nopad = nopadtext;
23020 if (lastnode == 'SPAN') {
23024 if (currentElementChild.nodeName == '#text') {
23025 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23026 toadd = nopadtext ? toadd : toadd.trim();
23027 if (!nopad && toadd.length > 80) {
23028 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23030 innerHTML += toadd;
23033 currentElementChild = currentElement.childNodes.item(i);
23039 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23041 // Recursively traverse the tree structure of the child node
23042 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23043 lastnode = currentElementChild.nodeName;
23045 currentElementChild=currentElement.childNodes.item(i);
23051 // The remaining code is mostly for formatting the tree
23052 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23057 ret+= "</"+tagName+">";
23063 applyBlacklists : function()
23065 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23066 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23070 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23071 if (b.indexOf(tag) > -1) {
23074 this.white.push(tag);
23078 Roo.each(w, function(tag) {
23079 if (b.indexOf(tag) > -1) {
23082 if (this.white.indexOf(tag) > -1) {
23085 this.white.push(tag);
23090 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23091 if (w.indexOf(tag) > -1) {
23094 this.black.push(tag);
23098 Roo.each(b, function(tag) {
23099 if (w.indexOf(tag) > -1) {
23102 if (this.black.indexOf(tag) > -1) {
23105 this.black.push(tag);
23110 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23111 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23115 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23116 if (b.indexOf(tag) > -1) {
23119 this.cwhite.push(tag);
23123 Roo.each(w, function(tag) {
23124 if (b.indexOf(tag) > -1) {
23127 if (this.cwhite.indexOf(tag) > -1) {
23130 this.cwhite.push(tag);
23135 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23136 if (w.indexOf(tag) > -1) {
23139 this.cblack.push(tag);
23143 Roo.each(b, function(tag) {
23144 if (w.indexOf(tag) > -1) {
23147 if (this.cblack.indexOf(tag) > -1) {
23150 this.cblack.push(tag);
23155 setStylesheets : function(stylesheets)
23157 if(typeof(stylesheets) == 'string'){
23158 Roo.get(this.iframe.contentDocument.head).createChild({
23160 rel : 'stylesheet',
23169 Roo.each(stylesheets, function(s) {
23174 Roo.get(_this.iframe.contentDocument.head).createChild({
23176 rel : 'stylesheet',
23185 removeStylesheets : function()
23189 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23194 setStyle : function(style)
23196 Roo.get(this.iframe.contentDocument.head).createChild({
23205 // hide stuff that is not compatible
23219 * @event specialkey
23223 * @cfg {String} fieldClass @hide
23226 * @cfg {String} focusClass @hide
23229 * @cfg {String} autoCreate @hide
23232 * @cfg {String} inputType @hide
23235 * @cfg {String} invalidClass @hide
23238 * @cfg {String} invalidText @hide
23241 * @cfg {String} msgFx @hide
23244 * @cfg {String} validateOnBlur @hide
23248 Roo.HtmlEditorCore.white = [
23249 'area', 'br', 'img', 'input', 'hr', 'wbr',
23251 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23252 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23253 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23254 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23255 'table', 'ul', 'xmp',
23257 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23260 'dir', 'menu', 'ol', 'ul', 'dl',
23266 Roo.HtmlEditorCore.black = [
23267 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23269 'base', 'basefont', 'bgsound', 'blink', 'body',
23270 'frame', 'frameset', 'head', 'html', 'ilayer',
23271 'iframe', 'layer', 'link', 'meta', 'object',
23272 'script', 'style' ,'title', 'xml' // clean later..
23274 Roo.HtmlEditorCore.clean = [
23275 'script', 'style', 'title', 'xml'
23277 Roo.HtmlEditorCore.remove = [
23282 Roo.HtmlEditorCore.ablack = [
23286 Roo.HtmlEditorCore.aclean = [
23287 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23291 Roo.HtmlEditorCore.pwhite= [
23292 'http', 'https', 'mailto'
23295 // white listed style attributes.
23296 Roo.HtmlEditorCore.cwhite= [
23297 // 'text-align', /// default is to allow most things..
23303 // black listed style attributes.
23304 Roo.HtmlEditorCore.cblack= [
23305 // 'font-size' -- this can be set by the project
23309 Roo.HtmlEditorCore.swapCodes =[
23328 * @class Roo.bootstrap.HtmlEditor
23329 * @extends Roo.bootstrap.TextArea
23330 * Bootstrap HtmlEditor class
23333 * Create a new HtmlEditor
23334 * @param {Object} config The config object
23337 Roo.bootstrap.HtmlEditor = function(config){
23338 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23339 if (!this.toolbars) {
23340 this.toolbars = [];
23343 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23346 * @event initialize
23347 * Fires when the editor is fully initialized (including the iframe)
23348 * @param {HtmlEditor} this
23353 * Fires when the editor is first receives the focus. Any insertion must wait
23354 * until after this event.
23355 * @param {HtmlEditor} this
23359 * @event beforesync
23360 * Fires before the textarea is updated with content from the editor iframe. Return false
23361 * to cancel the sync.
23362 * @param {HtmlEditor} this
23363 * @param {String} html
23367 * @event beforepush
23368 * Fires before the iframe editor is updated with content from the textarea. Return false
23369 * to cancel the push.
23370 * @param {HtmlEditor} this
23371 * @param {String} html
23376 * Fires when the textarea is updated with content from the editor iframe.
23377 * @param {HtmlEditor} this
23378 * @param {String} html
23383 * Fires when the iframe editor is updated with content from the textarea.
23384 * @param {HtmlEditor} this
23385 * @param {String} html
23389 * @event editmodechange
23390 * Fires when the editor switches edit modes
23391 * @param {HtmlEditor} this
23392 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23394 editmodechange: true,
23396 * @event editorevent
23397 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23398 * @param {HtmlEditor} this
23402 * @event firstfocus
23403 * Fires when on first focus - needed by toolbars..
23404 * @param {HtmlEditor} this
23409 * Auto save the htmlEditor value as a file into Events
23410 * @param {HtmlEditor} this
23414 * @event savedpreview
23415 * preview the saved version of htmlEditor
23416 * @param {HtmlEditor} this
23423 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23427 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23432 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23437 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23442 * @cfg {Number} height (in pixels)
23446 * @cfg {Number} width (in pixels)
23451 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23454 stylesheets: false,
23459 // private properties
23460 validationEvent : false,
23462 initialized : false,
23465 onFocus : Roo.emptyFn,
23467 hideMode:'offsets',
23469 tbContainer : false,
23473 toolbarContainer :function() {
23474 return this.wrap.select('.x-html-editor-tb',true).first();
23478 * Protected method that will not generally be called directly. It
23479 * is called when the editor creates its toolbar. Override this method if you need to
23480 * add custom toolbar buttons.
23481 * @param {HtmlEditor} editor
23483 createToolbar : function(){
23484 Roo.log('renewing');
23485 Roo.log("create toolbars");
23487 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23488 this.toolbars[0].render(this.toolbarContainer());
23492 // if (!editor.toolbars || !editor.toolbars.length) {
23493 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23496 // for (var i =0 ; i < editor.toolbars.length;i++) {
23497 // editor.toolbars[i] = Roo.factory(
23498 // typeof(editor.toolbars[i]) == 'string' ?
23499 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23500 // Roo.bootstrap.HtmlEditor);
23501 // editor.toolbars[i].init(editor);
23507 onRender : function(ct, position)
23509 // Roo.log("Call onRender: " + this.xtype);
23511 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23513 this.wrap = this.inputEl().wrap({
23514 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23517 this.editorcore.onRender(ct, position);
23519 if (this.resizable) {
23520 this.resizeEl = new Roo.Resizable(this.wrap, {
23524 minHeight : this.height,
23525 height: this.height,
23526 handles : this.resizable,
23529 resize : function(r, w, h) {
23530 _t.onResize(w,h); // -something
23536 this.createToolbar(this);
23539 if(!this.width && this.resizable){
23540 this.setSize(this.wrap.getSize());
23542 if (this.resizeEl) {
23543 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23544 // should trigger onReize..
23550 onResize : function(w, h)
23552 Roo.log('resize: ' +w + ',' + h );
23553 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23557 if(this.inputEl() ){
23558 if(typeof w == 'number'){
23559 var aw = w - this.wrap.getFrameWidth('lr');
23560 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23563 if(typeof h == 'number'){
23564 var tbh = -11; // fixme it needs to tool bar size!
23565 for (var i =0; i < this.toolbars.length;i++) {
23566 // fixme - ask toolbars for heights?
23567 tbh += this.toolbars[i].el.getHeight();
23568 //if (this.toolbars[i].footer) {
23569 // tbh += this.toolbars[i].footer.el.getHeight();
23577 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23578 ah -= 5; // knock a few pixes off for look..
23579 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23583 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23584 this.editorcore.onResize(ew,eh);
23589 * Toggles the editor between standard and source edit mode.
23590 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23592 toggleSourceEdit : function(sourceEditMode)
23594 this.editorcore.toggleSourceEdit(sourceEditMode);
23596 if(this.editorcore.sourceEditMode){
23597 Roo.log('editor - showing textarea');
23600 // Roo.log(this.syncValue());
23602 this.inputEl().removeClass(['hide', 'x-hidden']);
23603 this.inputEl().dom.removeAttribute('tabIndex');
23604 this.inputEl().focus();
23606 Roo.log('editor - hiding textarea');
23608 // Roo.log(this.pushValue());
23611 this.inputEl().addClass(['hide', 'x-hidden']);
23612 this.inputEl().dom.setAttribute('tabIndex', -1);
23613 //this.deferFocus();
23616 if(this.resizable){
23617 this.setSize(this.wrap.getSize());
23620 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23623 // private (for BoxComponent)
23624 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23626 // private (for BoxComponent)
23627 getResizeEl : function(){
23631 // private (for BoxComponent)
23632 getPositionEl : function(){
23637 initEvents : function(){
23638 this.originalValue = this.getValue();
23642 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23645 // markInvalid : Roo.emptyFn,
23647 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23650 // clearInvalid : Roo.emptyFn,
23652 setValue : function(v){
23653 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23654 this.editorcore.pushValue();
23659 deferFocus : function(){
23660 this.focus.defer(10, this);
23664 focus : function(){
23665 this.editorcore.focus();
23671 onDestroy : function(){
23677 for (var i =0; i < this.toolbars.length;i++) {
23678 // fixme - ask toolbars for heights?
23679 this.toolbars[i].onDestroy();
23682 this.wrap.dom.innerHTML = '';
23683 this.wrap.remove();
23688 onFirstFocus : function(){
23689 //Roo.log("onFirstFocus");
23690 this.editorcore.onFirstFocus();
23691 for (var i =0; i < this.toolbars.length;i++) {
23692 this.toolbars[i].onFirstFocus();
23698 syncValue : function()
23700 this.editorcore.syncValue();
23703 pushValue : function()
23705 this.editorcore.pushValue();
23709 // hide stuff that is not compatible
23723 * @event specialkey
23727 * @cfg {String} fieldClass @hide
23730 * @cfg {String} focusClass @hide
23733 * @cfg {String} autoCreate @hide
23736 * @cfg {String} inputType @hide
23739 * @cfg {String} invalidClass @hide
23742 * @cfg {String} invalidText @hide
23745 * @cfg {String} msgFx @hide
23748 * @cfg {String} validateOnBlur @hide
23757 Roo.namespace('Roo.bootstrap.htmleditor');
23759 * @class Roo.bootstrap.HtmlEditorToolbar1
23764 new Roo.bootstrap.HtmlEditor({
23767 new Roo.bootstrap.HtmlEditorToolbar1({
23768 disable : { fonts: 1 , format: 1, ..., ... , ...],
23774 * @cfg {Object} disable List of elements to disable..
23775 * @cfg {Array} btns List of additional buttons.
23779 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23782 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23785 Roo.apply(this, config);
23787 // default disabled, based on 'good practice'..
23788 this.disable = this.disable || {};
23789 Roo.applyIf(this.disable, {
23792 specialElements : true
23794 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23796 this.editor = config.editor;
23797 this.editorcore = config.editor.editorcore;
23799 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23801 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23802 // dont call parent... till later.
23804 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23809 editorcore : false,
23814 "h1","h2","h3","h4","h5","h6",
23816 "abbr", "acronym", "address", "cite", "samp", "var",
23820 onRender : function(ct, position)
23822 // Roo.log("Call onRender: " + this.xtype);
23824 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23826 this.el.dom.style.marginBottom = '0';
23828 var editorcore = this.editorcore;
23829 var editor= this.editor;
23832 var btn = function(id,cmd , toggle, handler, html){
23834 var event = toggle ? 'toggle' : 'click';
23839 xns: Roo.bootstrap,
23842 enableToggle:toggle !== false,
23844 pressed : toggle ? false : null,
23847 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23848 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23854 // var cb_box = function...
23859 xns: Roo.bootstrap,
23860 glyphicon : 'font',
23864 xns: Roo.bootstrap,
23868 Roo.each(this.formats, function(f) {
23869 style.menu.items.push({
23871 xns: Roo.bootstrap,
23872 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23877 editorcore.insertTag(this.tagname);
23884 children.push(style);
23886 btn('bold',false,true);
23887 btn('italic',false,true);
23888 btn('align-left', 'justifyleft',true);
23889 btn('align-center', 'justifycenter',true);
23890 btn('align-right' , 'justifyright',true);
23891 btn('link', false, false, function(btn) {
23892 //Roo.log("create link?");
23893 var url = prompt(this.createLinkText, this.defaultLinkValue);
23894 if(url && url != 'http:/'+'/'){
23895 this.editorcore.relayCmd('createlink', url);
23898 btn('list','insertunorderedlist',true);
23899 btn('pencil', false,true, function(btn){
23901 this.toggleSourceEdit(btn.pressed);
23904 if (this.editor.btns.length > 0) {
23905 for (var i = 0; i<this.editor.btns.length; i++) {
23906 children.push(this.editor.btns[i]);
23914 xns: Roo.bootstrap,
23919 xns: Roo.bootstrap,
23924 cog.menu.items.push({
23926 xns: Roo.bootstrap,
23927 html : Clean styles,
23932 editorcore.insertTag(this.tagname);
23941 this.xtype = 'NavSimplebar';
23943 for(var i=0;i< children.length;i++) {
23945 this.buttons.add(this.addxtypeChild(children[i]));
23949 editor.on('editorevent', this.updateToolbar, this);
23951 onBtnClick : function(id)
23953 this.editorcore.relayCmd(id);
23954 this.editorcore.focus();
23958 * Protected method that will not generally be called directly. It triggers
23959 * a toolbar update by reading the markup state of the current selection in the editor.
23961 updateToolbar: function(){
23963 if(!this.editorcore.activated){
23964 this.editor.onFirstFocus(); // is this neeed?
23968 var btns = this.buttons;
23969 var doc = this.editorcore.doc;
23970 btns.get('bold').setActive(doc.queryCommandState('bold'));
23971 btns.get('italic').setActive(doc.queryCommandState('italic'));
23972 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23974 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23975 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23976 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23978 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23979 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23982 var ans = this.editorcore.getAllAncestors();
23983 if (this.formatCombo) {
23986 var store = this.formatCombo.store;
23987 this.formatCombo.setValue("");
23988 for (var i =0; i < ans.length;i++) {
23989 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23991 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23999 // hides menus... - so this cant be on a menu...
24000 Roo.bootstrap.MenuMgr.hideAll();
24002 Roo.bootstrap.MenuMgr.hideAll();
24003 //this.editorsyncValue();
24005 onFirstFocus: function() {
24006 this.buttons.each(function(item){
24010 toggleSourceEdit : function(sourceEditMode){
24013 if(sourceEditMode){
24014 Roo.log("disabling buttons");
24015 this.buttons.each( function(item){
24016 if(item.cmd != 'pencil'){
24022 Roo.log("enabling buttons");
24023 if(this.editorcore.initialized){
24024 this.buttons.each( function(item){
24030 Roo.log("calling toggole on editor");
24031 // tell the editor that it's been pressed..
24032 this.editor.toggleSourceEdit(sourceEditMode);
24042 * @class Roo.bootstrap.Table.AbstractSelectionModel
24043 * @extends Roo.util.Observable
24044 * Abstract base class for grid SelectionModels. It provides the interface that should be
24045 * implemented by descendant classes. This class should not be directly instantiated.
24048 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24049 this.locked = false;
24050 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24054 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24055 /** @ignore Called by the grid automatically. Do not call directly. */
24056 init : function(grid){
24062 * Locks the selections.
24065 this.locked = true;
24069 * Unlocks the selections.
24071 unlock : function(){
24072 this.locked = false;
24076 * Returns true if the selections are locked.
24077 * @return {Boolean}
24079 isLocked : function(){
24080 return this.locked;
24084 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24085 * @class Roo.bootstrap.Table.RowSelectionModel
24086 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24087 * It supports multiple selections and keyboard selection/navigation.
24089 * @param {Object} config
24092 Roo.bootstrap.Table.RowSelectionModel = function(config){
24093 Roo.apply(this, config);
24094 this.selections = new Roo.util.MixedCollection(false, function(o){
24099 this.lastActive = false;
24103 * @event selectionchange
24104 * Fires when the selection changes
24105 * @param {SelectionModel} this
24107 "selectionchange" : true,
24109 * @event afterselectionchange
24110 * Fires after the selection changes (eg. by key press or clicking)
24111 * @param {SelectionModel} this
24113 "afterselectionchange" : true,
24115 * @event beforerowselect
24116 * Fires when a row is selected being selected, return false to cancel.
24117 * @param {SelectionModel} this
24118 * @param {Number} rowIndex The selected index
24119 * @param {Boolean} keepExisting False if other selections will be cleared
24121 "beforerowselect" : true,
24124 * Fires when a row is selected.
24125 * @param {SelectionModel} this
24126 * @param {Number} rowIndex The selected index
24127 * @param {Roo.data.Record} r The record
24129 "rowselect" : true,
24131 * @event rowdeselect
24132 * Fires when a row is deselected.
24133 * @param {SelectionModel} this
24134 * @param {Number} rowIndex The selected index
24136 "rowdeselect" : true
24138 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24139 this.locked = false;
24142 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24144 * @cfg {Boolean} singleSelect
24145 * True to allow selection of only one row at a time (defaults to false)
24147 singleSelect : false,
24150 initEvents : function()
24153 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24154 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24155 //}else{ // allow click to work like normal
24156 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24158 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24159 this.grid.on("rowclick", this.handleMouseDown, this);
24161 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24162 "up" : function(e){
24164 this.selectPrevious(e.shiftKey);
24165 }else if(this.last !== false && this.lastActive !== false){
24166 var last = this.last;
24167 this.selectRange(this.last, this.lastActive-1);
24168 this.grid.getView().focusRow(this.lastActive);
24169 if(last !== false){
24173 this.selectFirstRow();
24175 this.fireEvent("afterselectionchange", this);
24177 "down" : function(e){
24179 this.selectNext(e.shiftKey);
24180 }else if(this.last !== false && this.lastActive !== false){
24181 var last = this.last;
24182 this.selectRange(this.last, this.lastActive+1);
24183 this.grid.getView().focusRow(this.lastActive);
24184 if(last !== false){
24188 this.selectFirstRow();
24190 this.fireEvent("afterselectionchange", this);
24194 this.grid.store.on('load', function(){
24195 this.selections.clear();
24198 var view = this.grid.view;
24199 view.on("refresh", this.onRefresh, this);
24200 view.on("rowupdated", this.onRowUpdated, this);
24201 view.on("rowremoved", this.onRemove, this);
24206 onRefresh : function()
24208 var ds = this.grid.store, i, v = this.grid.view;
24209 var s = this.selections;
24210 s.each(function(r){
24211 if((i = ds.indexOfId(r.id)) != -1){
24220 onRemove : function(v, index, r){
24221 this.selections.remove(r);
24225 onRowUpdated : function(v, index, r){
24226 if(this.isSelected(r)){
24227 v.onRowSelect(index);
24233 * @param {Array} records The records to select
24234 * @param {Boolean} keepExisting (optional) True to keep existing selections
24236 selectRecords : function(records, keepExisting)
24239 this.clearSelections();
24241 var ds = this.grid.store;
24242 for(var i = 0, len = records.length; i < len; i++){
24243 this.selectRow(ds.indexOf(records[i]), true);
24248 * Gets the number of selected rows.
24251 getCount : function(){
24252 return this.selections.length;
24256 * Selects the first row in the grid.
24258 selectFirstRow : function(){
24263 * Select the last row.
24264 * @param {Boolean} keepExisting (optional) True to keep existing selections
24266 selectLastRow : function(keepExisting){
24267 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24268 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24272 * Selects the row immediately following the last selected row.
24273 * @param {Boolean} keepExisting (optional) True to keep existing selections
24275 selectNext : function(keepExisting)
24277 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24278 this.selectRow(this.last+1, keepExisting);
24279 this.grid.getView().focusRow(this.last);
24284 * Selects the row that precedes the last selected row.
24285 * @param {Boolean} keepExisting (optional) True to keep existing selections
24287 selectPrevious : function(keepExisting){
24289 this.selectRow(this.last-1, keepExisting);
24290 this.grid.getView().focusRow(this.last);
24295 * Returns the selected records
24296 * @return {Array} Array of selected records
24298 getSelections : function(){
24299 return [].concat(this.selections.items);
24303 * Returns the first selected record.
24306 getSelected : function(){
24307 return this.selections.itemAt(0);
24312 * Clears all selections.
24314 clearSelections : function(fast)
24320 var ds = this.grid.store;
24321 var s = this.selections;
24322 s.each(function(r){
24323 this.deselectRow(ds.indexOfId(r.id));
24327 this.selections.clear();
24334 * Selects all rows.
24336 selectAll : function(){
24340 this.selections.clear();
24341 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24342 this.selectRow(i, true);
24347 * Returns True if there is a selection.
24348 * @return {Boolean}
24350 hasSelection : function(){
24351 return this.selections.length > 0;
24355 * Returns True if the specified row is selected.
24356 * @param {Number/Record} record The record or index of the record to check
24357 * @return {Boolean}
24359 isSelected : function(index){
24360 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24361 return (r && this.selections.key(r.id) ? true : false);
24365 * Returns True if the specified record id is selected.
24366 * @param {String} id The id of record to check
24367 * @return {Boolean}
24369 isIdSelected : function(id){
24370 return (this.selections.key(id) ? true : false);
24375 handleMouseDBClick : function(e, t){
24379 handleMouseDown : function(e, t)
24381 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24382 if(this.isLocked() || rowIndex < 0 ){
24385 if(e.shiftKey && this.last !== false){
24386 var last = this.last;
24387 this.selectRange(last, rowIndex, e.ctrlKey);
24388 this.last = last; // reset the last
24392 var isSelected = this.isSelected(rowIndex);
24393 //Roo.log("select row:" + rowIndex);
24395 this.deselectRow(rowIndex);
24397 this.selectRow(rowIndex, true);
24401 if(e.button !== 0 && isSelected){
24402 alert('rowIndex 2: ' + rowIndex);
24403 view.focusRow(rowIndex);
24404 }else if(e.ctrlKey && isSelected){
24405 this.deselectRow(rowIndex);
24406 }else if(!isSelected){
24407 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24408 view.focusRow(rowIndex);
24412 this.fireEvent("afterselectionchange", this);
24415 handleDragableRowClick : function(grid, rowIndex, e)
24417 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24418 this.selectRow(rowIndex, false);
24419 grid.view.focusRow(rowIndex);
24420 this.fireEvent("afterselectionchange", this);
24425 * Selects multiple rows.
24426 * @param {Array} rows Array of the indexes of the row to select
24427 * @param {Boolean} keepExisting (optional) True to keep existing selections
24429 selectRows : function(rows, keepExisting){
24431 this.clearSelections();
24433 for(var i = 0, len = rows.length; i < len; i++){
24434 this.selectRow(rows[i], true);
24439 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24440 * @param {Number} startRow The index of the first row in the range
24441 * @param {Number} endRow The index of the last row in the range
24442 * @param {Boolean} keepExisting (optional) True to retain existing selections
24444 selectRange : function(startRow, endRow, keepExisting){
24449 this.clearSelections();
24451 if(startRow <= endRow){
24452 for(var i = startRow; i <= endRow; i++){
24453 this.selectRow(i, true);
24456 for(var i = startRow; i >= endRow; i--){
24457 this.selectRow(i, true);
24463 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24464 * @param {Number} startRow The index of the first row in the range
24465 * @param {Number} endRow The index of the last row in the range
24467 deselectRange : function(startRow, endRow, preventViewNotify){
24471 for(var i = startRow; i <= endRow; i++){
24472 this.deselectRow(i, preventViewNotify);
24478 * @param {Number} row The index of the row to select
24479 * @param {Boolean} keepExisting (optional) True to keep existing selections
24481 selectRow : function(index, keepExisting, preventViewNotify)
24483 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24486 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24487 if(!keepExisting || this.singleSelect){
24488 this.clearSelections();
24491 var r = this.grid.store.getAt(index);
24492 //console.log('selectRow - record id :' + r.id);
24494 this.selections.add(r);
24495 this.last = this.lastActive = index;
24496 if(!preventViewNotify){
24497 var proxy = new Roo.Element(
24498 this.grid.getRowDom(index)
24500 proxy.addClass('bg-info info');
24502 this.fireEvent("rowselect", this, index, r);
24503 this.fireEvent("selectionchange", this);
24509 * @param {Number} row The index of the row to deselect
24511 deselectRow : function(index, preventViewNotify)
24516 if(this.last == index){
24519 if(this.lastActive == index){
24520 this.lastActive = false;
24523 var r = this.grid.store.getAt(index);
24528 this.selections.remove(r);
24529 //.console.log('deselectRow - record id :' + r.id);
24530 if(!preventViewNotify){
24532 var proxy = new Roo.Element(
24533 this.grid.getRowDom(index)
24535 proxy.removeClass('bg-info info');
24537 this.fireEvent("rowdeselect", this, index);
24538 this.fireEvent("selectionchange", this);
24542 restoreLast : function(){
24544 this.last = this._last;
24549 acceptsNav : function(row, col, cm){
24550 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24554 onEditorKey : function(field, e){
24555 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24560 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24562 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24564 }else if(k == e.ENTER && !e.ctrlKey){
24568 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24570 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24572 }else if(k == e.ESC){
24576 g.startEditing(newCell[0], newCell[1]);
24582 * Ext JS Library 1.1.1
24583 * Copyright(c) 2006-2007, Ext JS, LLC.
24585 * Originally Released Under LGPL - original licence link has changed is not relivant.
24588 * <script type="text/javascript">
24592 * @class Roo.bootstrap.PagingToolbar
24593 * @extends Roo.bootstrap.NavSimplebar
24594 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24596 * Create a new PagingToolbar
24597 * @param {Object} config The config object
24598 * @param {Roo.data.Store} store
24600 Roo.bootstrap.PagingToolbar = function(config)
24602 // old args format still supported... - xtype is prefered..
24603 // created from xtype...
24605 this.ds = config.dataSource;
24607 if (config.store && !this.ds) {
24608 this.store= Roo.factory(config.store, Roo.data);
24609 this.ds = this.store;
24610 this.ds.xmodule = this.xmodule || false;
24613 this.toolbarItems = [];
24614 if (config.items) {
24615 this.toolbarItems = config.items;
24618 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24623 this.bind(this.ds);
24626 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24630 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24632 * @cfg {Roo.data.Store} dataSource
24633 * The underlying data store providing the paged data
24636 * @cfg {String/HTMLElement/Element} container
24637 * container The id or element that will contain the toolbar
24640 * @cfg {Boolean} displayInfo
24641 * True to display the displayMsg (defaults to false)
24644 * @cfg {Number} pageSize
24645 * The number of records to display per page (defaults to 20)
24649 * @cfg {String} displayMsg
24650 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24652 displayMsg : 'Displaying {0} - {1} of {2}',
24654 * @cfg {String} emptyMsg
24655 * The message to display when no records are found (defaults to "No data to display")
24657 emptyMsg : 'No data to display',
24659 * Customizable piece of the default paging text (defaults to "Page")
24662 beforePageText : "Page",
24664 * Customizable piece of the default paging text (defaults to "of %0")
24667 afterPageText : "of {0}",
24669 * Customizable piece of the default paging text (defaults to "First Page")
24672 firstText : "First Page",
24674 * Customizable piece of the default paging text (defaults to "Previous Page")
24677 prevText : "Previous Page",
24679 * Customizable piece of the default paging text (defaults to "Next Page")
24682 nextText : "Next Page",
24684 * Customizable piece of the default paging text (defaults to "Last Page")
24687 lastText : "Last Page",
24689 * Customizable piece of the default paging text (defaults to "Refresh")
24692 refreshText : "Refresh",
24696 onRender : function(ct, position)
24698 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24699 this.navgroup.parentId = this.id;
24700 this.navgroup.onRender(this.el, null);
24701 // add the buttons to the navgroup
24703 if(this.displayInfo){
24704 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24705 this.displayEl = this.el.select('.x-paging-info', true).first();
24706 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24707 // this.displayEl = navel.el.select('span',true).first();
24713 Roo.each(_this.buttons, function(e){ // this might need to use render????
24714 Roo.factory(e).render(_this.el);
24718 Roo.each(_this.toolbarItems, function(e) {
24719 _this.navgroup.addItem(e);
24723 this.first = this.navgroup.addItem({
24724 tooltip: this.firstText,
24726 icon : 'fa fa-backward',
24728 preventDefault: true,
24729 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24732 this.prev = this.navgroup.addItem({
24733 tooltip: this.prevText,
24735 icon : 'fa fa-step-backward',
24737 preventDefault: true,
24738 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24740 //this.addSeparator();
24743 var field = this.navgroup.addItem( {
24745 cls : 'x-paging-position',
24747 html : this.beforePageText +
24748 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24749 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24752 this.field = field.el.select('input', true).first();
24753 this.field.on("keydown", this.onPagingKeydown, this);
24754 this.field.on("focus", function(){this.dom.select();});
24757 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24758 //this.field.setHeight(18);
24759 //this.addSeparator();
24760 this.next = this.navgroup.addItem({
24761 tooltip: this.nextText,
24763 html : ' <i class="fa fa-step-forward">',
24765 preventDefault: true,
24766 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24768 this.last = this.navgroup.addItem({
24769 tooltip: this.lastText,
24770 icon : 'fa fa-forward',
24773 preventDefault: true,
24774 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24776 //this.addSeparator();
24777 this.loading = this.navgroup.addItem({
24778 tooltip: this.refreshText,
24779 icon: 'fa fa-refresh',
24780 preventDefault: true,
24781 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24787 updateInfo : function(){
24788 if(this.displayEl){
24789 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24790 var msg = count == 0 ?
24794 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24796 this.displayEl.update(msg);
24801 onLoad : function(ds, r, o)
24803 this.cursor = o.params.start ? o.params.start : 0;
24805 var d = this.getPageData(),
24810 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24811 this.field.dom.value = ap;
24812 this.first.setDisabled(ap == 1);
24813 this.prev.setDisabled(ap == 1);
24814 this.next.setDisabled(ap == ps);
24815 this.last.setDisabled(ap == ps);
24816 this.loading.enable();
24821 getPageData : function(){
24822 var total = this.ds.getTotalCount();
24825 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24826 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24831 onLoadError : function(){
24832 this.loading.enable();
24836 onPagingKeydown : function(e){
24837 var k = e.getKey();
24838 var d = this.getPageData();
24840 var v = this.field.dom.value, pageNum;
24841 if(!v || isNaN(pageNum = parseInt(v, 10))){
24842 this.field.dom.value = d.activePage;
24845 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24846 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24849 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))
24851 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24852 this.field.dom.value = pageNum;
24853 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24856 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24858 var v = this.field.dom.value, pageNum;
24859 var increment = (e.shiftKey) ? 10 : 1;
24860 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24863 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24864 this.field.dom.value = d.activePage;
24867 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24869 this.field.dom.value = parseInt(v, 10) + increment;
24870 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24871 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24878 beforeLoad : function(){
24880 this.loading.disable();
24885 onClick : function(which){
24894 ds.load({params:{start: 0, limit: this.pageSize}});
24897 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24900 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24903 var total = ds.getTotalCount();
24904 var extra = total % this.pageSize;
24905 var lastStart = extra ? (total - extra) : total-this.pageSize;
24906 ds.load({params:{start: lastStart, limit: this.pageSize}});
24909 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24915 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24916 * @param {Roo.data.Store} store The data store to unbind
24918 unbind : function(ds){
24919 ds.un("beforeload", this.beforeLoad, this);
24920 ds.un("load", this.onLoad, this);
24921 ds.un("loadexception", this.onLoadError, this);
24922 ds.un("remove", this.updateInfo, this);
24923 ds.un("add", this.updateInfo, this);
24924 this.ds = undefined;
24928 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24929 * @param {Roo.data.Store} store The data store to bind
24931 bind : function(ds){
24932 ds.on("beforeload", this.beforeLoad, this);
24933 ds.on("load", this.onLoad, this);
24934 ds.on("loadexception", this.onLoadError, this);
24935 ds.on("remove", this.updateInfo, this);
24936 ds.on("add", this.updateInfo, this);
24947 * @class Roo.bootstrap.MessageBar
24948 * @extends Roo.bootstrap.Component
24949 * Bootstrap MessageBar class
24950 * @cfg {String} html contents of the MessageBar
24951 * @cfg {String} weight (info | success | warning | danger) default info
24952 * @cfg {String} beforeClass insert the bar before the given class
24953 * @cfg {Boolean} closable (true | false) default false
24954 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24957 * Create a new Element
24958 * @param {Object} config The config object
24961 Roo.bootstrap.MessageBar = function(config){
24962 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24965 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24971 beforeClass: 'bootstrap-sticky-wrap',
24973 getAutoCreate : function(){
24977 cls: 'alert alert-dismissable alert-' + this.weight,
24982 html: this.html || ''
24988 cfg.cls += ' alert-messages-fixed';
25002 onRender : function(ct, position)
25004 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25007 var cfg = Roo.apply({}, this.getAutoCreate());
25011 cfg.cls += ' ' + this.cls;
25014 cfg.style = this.style;
25016 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25018 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25021 this.el.select('>button.close').on('click', this.hide, this);
25027 if (!this.rendered) {
25033 this.fireEvent('show', this);
25039 if (!this.rendered) {
25045 this.fireEvent('hide', this);
25048 update : function()
25050 // var e = this.el.dom.firstChild;
25052 // if(this.closable){
25053 // e = e.nextSibling;
25056 // e.data = this.html || '';
25058 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25074 * @class Roo.bootstrap.Graph
25075 * @extends Roo.bootstrap.Component
25076 * Bootstrap Graph class
25080 @cfg {String} graphtype bar | vbar | pie
25081 @cfg {number} g_x coodinator | centre x (pie)
25082 @cfg {number} g_y coodinator | centre y (pie)
25083 @cfg {number} g_r radius (pie)
25084 @cfg {number} g_height height of the chart (respected by all elements in the set)
25085 @cfg {number} g_width width of the chart (respected by all elements in the set)
25086 @cfg {Object} title The title of the chart
25089 -opts (object) options for the chart
25091 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25092 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25094 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.
25095 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25097 o stretch (boolean)
25099 -opts (object) options for the pie
25102 o startAngle (number)
25103 o endAngle (number)
25107 * Create a new Input
25108 * @param {Object} config The config object
25111 Roo.bootstrap.Graph = function(config){
25112 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25118 * The img click event for the img.
25119 * @param {Roo.EventObject} e
25125 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25136 //g_colors: this.colors,
25143 getAutoCreate : function(){
25154 onRender : function(ct,position){
25157 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25159 if (typeof(Raphael) == 'undefined') {
25160 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25164 this.raphael = Raphael(this.el.dom);
25166 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25167 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25168 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25169 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25171 r.text(160, 10, "Single Series Chart").attr(txtattr);
25172 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25173 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25174 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25176 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25177 r.barchart(330, 10, 300, 220, data1);
25178 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25179 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25182 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25183 // r.barchart(30, 30, 560, 250, xdata, {
25184 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25185 // axis : "0 0 1 1",
25186 // axisxlabels : xdata
25187 // //yvalues : cols,
25190 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25192 // this.load(null,xdata,{
25193 // axis : "0 0 1 1",
25194 // axisxlabels : xdata
25199 load : function(graphtype,xdata,opts)
25201 this.raphael.clear();
25203 graphtype = this.graphtype;
25208 var r = this.raphael,
25209 fin = function () {
25210 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25212 fout = function () {
25213 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25215 pfin = function() {
25216 this.sector.stop();
25217 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25220 this.label[0].stop();
25221 this.label[0].attr({ r: 7.5 });
25222 this.label[1].attr({ "font-weight": 800 });
25225 pfout = function() {
25226 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25229 this.label[0].animate({ r: 5 }, 500, "bounce");
25230 this.label[1].attr({ "font-weight": 400 });
25236 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25239 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25242 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25243 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25245 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25252 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25257 setTitle: function(o)
25262 initEvents: function() {
25265 this.el.on('click', this.onClick, this);
25269 onClick : function(e)
25271 Roo.log('img onclick');
25272 this.fireEvent('click', this, e);
25284 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25287 * @class Roo.bootstrap.dash.NumberBox
25288 * @extends Roo.bootstrap.Component
25289 * Bootstrap NumberBox class
25290 * @cfg {String} headline Box headline
25291 * @cfg {String} content Box content
25292 * @cfg {String} icon Box icon
25293 * @cfg {String} footer Footer text
25294 * @cfg {String} fhref Footer href
25297 * Create a new NumberBox
25298 * @param {Object} config The config object
25302 Roo.bootstrap.dash.NumberBox = function(config){
25303 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25307 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25316 getAutoCreate : function(){
25320 cls : 'small-box ',
25328 cls : 'roo-headline',
25329 html : this.headline
25333 cls : 'roo-content',
25334 html : this.content
25348 cls : 'ion ' + this.icon
25357 cls : 'small-box-footer',
25358 href : this.fhref || '#',
25362 cfg.cn.push(footer);
25369 onRender : function(ct,position){
25370 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25377 setHeadline: function (value)
25379 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25382 setFooter: function (value, href)
25384 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25387 this.el.select('a.small-box-footer',true).first().attr('href', href);
25392 setContent: function (value)
25394 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25397 initEvents: function()
25411 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25414 * @class Roo.bootstrap.dash.TabBox
25415 * @extends Roo.bootstrap.Component
25416 * Bootstrap TabBox class
25417 * @cfg {String} title Title of the TabBox
25418 * @cfg {String} icon Icon of the TabBox
25419 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25420 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25423 * Create a new TabBox
25424 * @param {Object} config The config object
25428 Roo.bootstrap.dash.TabBox = function(config){
25429 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25434 * When a pane is added
25435 * @param {Roo.bootstrap.dash.TabPane} pane
25439 * @event activatepane
25440 * When a pane is activated
25441 * @param {Roo.bootstrap.dash.TabPane} pane
25443 "activatepane" : true
25451 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25456 tabScrollable : false,
25458 getChildContainer : function()
25460 return this.el.select('.tab-content', true).first();
25463 getAutoCreate : function(){
25467 cls: 'pull-left header',
25475 cls: 'fa ' + this.icon
25481 cls: 'nav nav-tabs pull-right',
25487 if(this.tabScrollable){
25494 cls: 'nav nav-tabs pull-right',
25505 cls: 'nav-tabs-custom',
25510 cls: 'tab-content no-padding',
25518 initEvents : function()
25520 //Roo.log('add add pane handler');
25521 this.on('addpane', this.onAddPane, this);
25524 * Updates the box title
25525 * @param {String} html to set the title to.
25527 setTitle : function(value)
25529 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25531 onAddPane : function(pane)
25533 this.panes.push(pane);
25534 //Roo.log('addpane');
25536 // tabs are rendere left to right..
25537 if(!this.showtabs){
25541 var ctr = this.el.select('.nav-tabs', true).first();
25544 var existing = ctr.select('.nav-tab',true);
25545 var qty = existing.getCount();;
25548 var tab = ctr.createChild({
25550 cls : 'nav-tab' + (qty ? '' : ' active'),
25558 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25561 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25563 pane.el.addClass('active');
25568 onTabClick : function(ev,un,ob,pane)
25570 //Roo.log('tab - prev default');
25571 ev.preventDefault();
25574 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25575 pane.tab.addClass('active');
25576 //Roo.log(pane.title);
25577 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25578 // technically we should have a deactivate event.. but maybe add later.
25579 // and it should not de-activate the selected tab...
25580 this.fireEvent('activatepane', pane);
25581 pane.el.addClass('active');
25582 pane.fireEvent('activate');
25587 getActivePane : function()
25590 Roo.each(this.panes, function(p) {
25591 if(p.el.hasClass('active')){
25612 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25614 * @class Roo.bootstrap.TabPane
25615 * @extends Roo.bootstrap.Component
25616 * Bootstrap TabPane class
25617 * @cfg {Boolean} active (false | true) Default false
25618 * @cfg {String} title title of panel
25622 * Create a new TabPane
25623 * @param {Object} config The config object
25626 Roo.bootstrap.dash.TabPane = function(config){
25627 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25633 * When a pane is activated
25634 * @param {Roo.bootstrap.dash.TabPane} pane
25641 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25646 // the tabBox that this is attached to.
25649 getAutoCreate : function()
25657 cfg.cls += ' active';
25662 initEvents : function()
25664 //Roo.log('trigger add pane handler');
25665 this.parent().fireEvent('addpane', this)
25669 * Updates the tab title
25670 * @param {String} html to set the title to.
25672 setTitle: function(str)
25678 this.tab.select('a', true).first().dom.innerHTML = str;
25695 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25698 * @class Roo.bootstrap.menu.Menu
25699 * @extends Roo.bootstrap.Component
25700 * Bootstrap Menu class - container for Menu
25701 * @cfg {String} html Text of the menu
25702 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25703 * @cfg {String} icon Font awesome icon
25704 * @cfg {String} pos Menu align to (top | bottom) default bottom
25708 * Create a new Menu
25709 * @param {Object} config The config object
25713 Roo.bootstrap.menu.Menu = function(config){
25714 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25718 * @event beforeshow
25719 * Fires before this menu is displayed
25720 * @param {Roo.bootstrap.menu.Menu} this
25724 * @event beforehide
25725 * Fires before this menu is hidden
25726 * @param {Roo.bootstrap.menu.Menu} this
25731 * Fires after this menu is displayed
25732 * @param {Roo.bootstrap.menu.Menu} this
25737 * Fires after this menu is hidden
25738 * @param {Roo.bootstrap.menu.Menu} this
25743 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25744 * @param {Roo.bootstrap.menu.Menu} this
25745 * @param {Roo.EventObject} e
25752 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25756 weight : 'default',
25761 getChildContainer : function() {
25762 if(this.isSubMenu){
25766 return this.el.select('ul.dropdown-menu', true).first();
25769 getAutoCreate : function()
25774 cls : 'roo-menu-text',
25782 cls : 'fa ' + this.icon
25793 cls : 'dropdown-button btn btn-' + this.weight,
25798 cls : 'dropdown-toggle btn btn-' + this.weight,
25808 cls : 'dropdown-menu'
25814 if(this.pos == 'top'){
25815 cfg.cls += ' dropup';
25818 if(this.isSubMenu){
25821 cls : 'dropdown-menu'
25828 onRender : function(ct, position)
25830 this.isSubMenu = ct.hasClass('dropdown-submenu');
25832 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25835 initEvents : function()
25837 if(this.isSubMenu){
25841 this.hidden = true;
25843 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25844 this.triggerEl.on('click', this.onTriggerPress, this);
25846 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25847 this.buttonEl.on('click', this.onClick, this);
25853 if(this.isSubMenu){
25857 return this.el.select('ul.dropdown-menu', true).first();
25860 onClick : function(e)
25862 this.fireEvent("click", this, e);
25865 onTriggerPress : function(e)
25867 if (this.isVisible()) {
25874 isVisible : function(){
25875 return !this.hidden;
25880 this.fireEvent("beforeshow", this);
25882 this.hidden = false;
25883 this.el.addClass('open');
25885 Roo.get(document).on("mouseup", this.onMouseUp, this);
25887 this.fireEvent("show", this);
25894 this.fireEvent("beforehide", this);
25896 this.hidden = true;
25897 this.el.removeClass('open');
25899 Roo.get(document).un("mouseup", this.onMouseUp);
25901 this.fireEvent("hide", this);
25904 onMouseUp : function()
25918 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25921 * @class Roo.bootstrap.menu.Item
25922 * @extends Roo.bootstrap.Component
25923 * Bootstrap MenuItem class
25924 * @cfg {Boolean} submenu (true | false) default false
25925 * @cfg {String} html text of the item
25926 * @cfg {String} href the link
25927 * @cfg {Boolean} disable (true | false) default false
25928 * @cfg {Boolean} preventDefault (true | false) default true
25929 * @cfg {String} icon Font awesome icon
25930 * @cfg {String} pos Submenu align to (left | right) default right
25934 * Create a new Item
25935 * @param {Object} config The config object
25939 Roo.bootstrap.menu.Item = function(config){
25940 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25944 * Fires when the mouse is hovering over this menu
25945 * @param {Roo.bootstrap.menu.Item} this
25946 * @param {Roo.EventObject} e
25951 * Fires when the mouse exits this menu
25952 * @param {Roo.bootstrap.menu.Item} this
25953 * @param {Roo.EventObject} e
25959 * The raw click event for the entire grid.
25960 * @param {Roo.EventObject} e
25966 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25971 preventDefault: true,
25976 getAutoCreate : function()
25981 cls : 'roo-menu-item-text',
25989 cls : 'fa ' + this.icon
25998 href : this.href || '#',
26005 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26009 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26011 if(this.pos == 'left'){
26012 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26019 initEvents : function()
26021 this.el.on('mouseover', this.onMouseOver, this);
26022 this.el.on('mouseout', this.onMouseOut, this);
26024 this.el.select('a', true).first().on('click', this.onClick, this);
26028 onClick : function(e)
26030 if(this.preventDefault){
26031 e.preventDefault();
26034 this.fireEvent("click", this, e);
26037 onMouseOver : function(e)
26039 if(this.submenu && this.pos == 'left'){
26040 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26043 this.fireEvent("mouseover", this, e);
26046 onMouseOut : function(e)
26048 this.fireEvent("mouseout", this, e);
26060 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26063 * @class Roo.bootstrap.menu.Separator
26064 * @extends Roo.bootstrap.Component
26065 * Bootstrap Separator class
26068 * Create a new Separator
26069 * @param {Object} config The config object
26073 Roo.bootstrap.menu.Separator = function(config){
26074 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26077 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26079 getAutoCreate : function(){
26100 * @class Roo.bootstrap.Tooltip
26101 * Bootstrap Tooltip class
26102 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26103 * to determine which dom element triggers the tooltip.
26105 * It needs to add support for additional attributes like tooltip-position
26108 * Create a new Toolti
26109 * @param {Object} config The config object
26112 Roo.bootstrap.Tooltip = function(config){
26113 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26115 this.alignment = Roo.bootstrap.Tooltip.alignment;
26117 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26118 this.alignment = config.alignment;
26123 Roo.apply(Roo.bootstrap.Tooltip, {
26125 * @function init initialize tooltip monitoring.
26129 currentTip : false,
26130 currentRegion : false,
26136 Roo.get(document).on('mouseover', this.enter ,this);
26137 Roo.get(document).on('mouseout', this.leave, this);
26140 this.currentTip = new Roo.bootstrap.Tooltip();
26143 enter : function(ev)
26145 var dom = ev.getTarget();
26147 //Roo.log(['enter',dom]);
26148 var el = Roo.fly(dom);
26149 if (this.currentEl) {
26151 //Roo.log(this.currentEl);
26152 //Roo.log(this.currentEl.contains(dom));
26153 if (this.currentEl == el) {
26156 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26162 if (this.currentTip.el) {
26163 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26167 if(!el || el.dom == document){
26173 // you can not look for children, as if el is the body.. then everythign is the child..
26174 if (!el.attr('tooltip')) { //
26175 if (!el.select("[tooltip]").elements.length) {
26178 // is the mouse over this child...?
26179 bindEl = el.select("[tooltip]").first();
26180 var xy = ev.getXY();
26181 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26182 //Roo.log("not in region.");
26185 //Roo.log("child element over..");
26188 this.currentEl = bindEl;
26189 this.currentTip.bind(bindEl);
26190 this.currentRegion = Roo.lib.Region.getRegion(dom);
26191 this.currentTip.enter();
26194 leave : function(ev)
26196 var dom = ev.getTarget();
26197 //Roo.log(['leave',dom]);
26198 if (!this.currentEl) {
26203 if (dom != this.currentEl.dom) {
26206 var xy = ev.getXY();
26207 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26210 // only activate leave if mouse cursor is outside... bounding box..
26215 if (this.currentTip) {
26216 this.currentTip.leave();
26218 //Roo.log('clear currentEl');
26219 this.currentEl = false;
26224 'left' : ['r-l', [-2,0], 'right'],
26225 'right' : ['l-r', [2,0], 'left'],
26226 'bottom' : ['t-b', [0,2], 'top'],
26227 'top' : [ 'b-t', [0,-2], 'bottom']
26233 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26238 delay : null, // can be { show : 300 , hide: 500}
26242 hoverState : null, //???
26244 placement : 'bottom',
26248 getAutoCreate : function(){
26255 cls : 'tooltip-arrow'
26258 cls : 'tooltip-inner'
26265 bind : function(el)
26271 enter : function () {
26273 if (this.timeout != null) {
26274 clearTimeout(this.timeout);
26277 this.hoverState = 'in';
26278 //Roo.log("enter - show");
26279 if (!this.delay || !this.delay.show) {
26284 this.timeout = setTimeout(function () {
26285 if (_t.hoverState == 'in') {
26288 }, this.delay.show);
26292 clearTimeout(this.timeout);
26294 this.hoverState = 'out';
26295 if (!this.delay || !this.delay.hide) {
26301 this.timeout = setTimeout(function () {
26302 //Roo.log("leave - timeout");
26304 if (_t.hoverState == 'out') {
26306 Roo.bootstrap.Tooltip.currentEl = false;
26311 show : function (msg)
26314 this.render(document.body);
26317 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26319 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26321 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26323 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26325 var placement = typeof this.placement == 'function' ?
26326 this.placement.call(this, this.el, on_el) :
26329 var autoToken = /\s?auto?\s?/i;
26330 var autoPlace = autoToken.test(placement);
26332 placement = placement.replace(autoToken, '') || 'top';
26336 //this.el.setXY([0,0]);
26338 //this.el.dom.style.display='block';
26340 //this.el.appendTo(on_el);
26342 var p = this.getPosition();
26343 var box = this.el.getBox();
26349 var align = this.alignment[placement];
26351 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26353 if(placement == 'top' || placement == 'bottom'){
26355 placement = 'right';
26358 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26359 placement = 'left';
26362 var scroll = Roo.select('body', true).first().getScroll();
26364 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26368 align = this.alignment[placement];
26371 this.el.alignTo(this.bindEl, align[0],align[1]);
26372 //var arrow = this.el.select('.arrow',true).first();
26373 //arrow.set(align[2],
26375 this.el.addClass(placement);
26377 this.el.addClass('in fade');
26379 this.hoverState = null;
26381 if (this.el.hasClass('fade')) {
26392 //this.el.setXY([0,0]);
26393 this.el.removeClass('in');
26409 * @class Roo.bootstrap.LocationPicker
26410 * @extends Roo.bootstrap.Component
26411 * Bootstrap LocationPicker class
26412 * @cfg {Number} latitude Position when init default 0
26413 * @cfg {Number} longitude Position when init default 0
26414 * @cfg {Number} zoom default 15
26415 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26416 * @cfg {Boolean} mapTypeControl default false
26417 * @cfg {Boolean} disableDoubleClickZoom default false
26418 * @cfg {Boolean} scrollwheel default true
26419 * @cfg {Boolean} streetViewControl default false
26420 * @cfg {Number} radius default 0
26421 * @cfg {String} locationName
26422 * @cfg {Boolean} draggable default true
26423 * @cfg {Boolean} enableAutocomplete default false
26424 * @cfg {Boolean} enableReverseGeocode default true
26425 * @cfg {String} markerTitle
26428 * Create a new LocationPicker
26429 * @param {Object} config The config object
26433 Roo.bootstrap.LocationPicker = function(config){
26435 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26440 * Fires when the picker initialized.
26441 * @param {Roo.bootstrap.LocationPicker} this
26442 * @param {Google Location} location
26446 * @event positionchanged
26447 * Fires when the picker position changed.
26448 * @param {Roo.bootstrap.LocationPicker} this
26449 * @param {Google Location} location
26451 positionchanged : true,
26454 * Fires when the map resize.
26455 * @param {Roo.bootstrap.LocationPicker} this
26460 * Fires when the map show.
26461 * @param {Roo.bootstrap.LocationPicker} this
26466 * Fires when the map hide.
26467 * @param {Roo.bootstrap.LocationPicker} this
26472 * Fires when click the map.
26473 * @param {Roo.bootstrap.LocationPicker} this
26474 * @param {Map event} e
26478 * @event mapRightClick
26479 * Fires when right click the map.
26480 * @param {Roo.bootstrap.LocationPicker} this
26481 * @param {Map event} e
26483 mapRightClick : true,
26485 * @event markerClick
26486 * Fires when click the marker.
26487 * @param {Roo.bootstrap.LocationPicker} this
26488 * @param {Map event} e
26490 markerClick : true,
26492 * @event markerRightClick
26493 * Fires when right click the marker.
26494 * @param {Roo.bootstrap.LocationPicker} this
26495 * @param {Map event} e
26497 markerRightClick : true,
26499 * @event OverlayViewDraw
26500 * Fires when OverlayView Draw
26501 * @param {Roo.bootstrap.LocationPicker} this
26503 OverlayViewDraw : true,
26505 * @event OverlayViewOnAdd
26506 * Fires when OverlayView Draw
26507 * @param {Roo.bootstrap.LocationPicker} this
26509 OverlayViewOnAdd : true,
26511 * @event OverlayViewOnRemove
26512 * Fires when OverlayView Draw
26513 * @param {Roo.bootstrap.LocationPicker} this
26515 OverlayViewOnRemove : true,
26517 * @event OverlayViewShow
26518 * Fires when OverlayView Draw
26519 * @param {Roo.bootstrap.LocationPicker} this
26520 * @param {Pixel} cpx
26522 OverlayViewShow : true,
26524 * @event OverlayViewHide
26525 * Fires when OverlayView Draw
26526 * @param {Roo.bootstrap.LocationPicker} this
26528 OverlayViewHide : true,
26530 * @event loadexception
26531 * Fires when load google lib failed.
26532 * @param {Roo.bootstrap.LocationPicker} this
26534 loadexception : true
26539 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26541 gMapContext: false,
26547 mapTypeControl: false,
26548 disableDoubleClickZoom: false,
26550 streetViewControl: false,
26554 enableAutocomplete: false,
26555 enableReverseGeocode: true,
26558 getAutoCreate: function()
26563 cls: 'roo-location-picker'
26569 initEvents: function(ct, position)
26571 if(!this.el.getWidth() || this.isApplied()){
26575 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26580 initial: function()
26582 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26583 this.fireEvent('loadexception', this);
26587 if(!this.mapTypeId){
26588 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26591 this.gMapContext = this.GMapContext();
26593 this.initOverlayView();
26595 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26599 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26600 _this.setPosition(_this.gMapContext.marker.position);
26603 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26604 _this.fireEvent('mapClick', this, event);
26608 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26609 _this.fireEvent('mapRightClick', this, event);
26613 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26614 _this.fireEvent('markerClick', this, event);
26618 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26619 _this.fireEvent('markerRightClick', this, event);
26623 this.setPosition(this.gMapContext.location);
26625 this.fireEvent('initial', this, this.gMapContext.location);
26628 initOverlayView: function()
26632 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26636 _this.fireEvent('OverlayViewDraw', _this);
26641 _this.fireEvent('OverlayViewOnAdd', _this);
26644 onRemove: function()
26646 _this.fireEvent('OverlayViewOnRemove', _this);
26649 show: function(cpx)
26651 _this.fireEvent('OverlayViewShow', _this, cpx);
26656 _this.fireEvent('OverlayViewHide', _this);
26662 fromLatLngToContainerPixel: function(event)
26664 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26667 isApplied: function()
26669 return this.getGmapContext() == false ? false : true;
26672 getGmapContext: function()
26674 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26677 GMapContext: function()
26679 var position = new google.maps.LatLng(this.latitude, this.longitude);
26681 var _map = new google.maps.Map(this.el.dom, {
26684 mapTypeId: this.mapTypeId,
26685 mapTypeControl: this.mapTypeControl,
26686 disableDoubleClickZoom: this.disableDoubleClickZoom,
26687 scrollwheel: this.scrollwheel,
26688 streetViewControl: this.streetViewControl,
26689 locationName: this.locationName,
26690 draggable: this.draggable,
26691 enableAutocomplete: this.enableAutocomplete,
26692 enableReverseGeocode: this.enableReverseGeocode
26695 var _marker = new google.maps.Marker({
26696 position: position,
26698 title: this.markerTitle,
26699 draggable: this.draggable
26706 location: position,
26707 radius: this.radius,
26708 locationName: this.locationName,
26709 addressComponents: {
26710 formatted_address: null,
26711 addressLine1: null,
26712 addressLine2: null,
26714 streetNumber: null,
26718 stateOrProvince: null
26721 domContainer: this.el.dom,
26722 geodecoder: new google.maps.Geocoder()
26726 drawCircle: function(center, radius, options)
26728 if (this.gMapContext.circle != null) {
26729 this.gMapContext.circle.setMap(null);
26733 options = Roo.apply({}, options, {
26734 strokeColor: "#0000FF",
26735 strokeOpacity: .35,
26737 fillColor: "#0000FF",
26741 options.map = this.gMapContext.map;
26742 options.radius = radius;
26743 options.center = center;
26744 this.gMapContext.circle = new google.maps.Circle(options);
26745 return this.gMapContext.circle;
26751 setPosition: function(location)
26753 this.gMapContext.location = location;
26754 this.gMapContext.marker.setPosition(location);
26755 this.gMapContext.map.panTo(location);
26756 this.drawCircle(location, this.gMapContext.radius, {});
26760 if (this.gMapContext.settings.enableReverseGeocode) {
26761 this.gMapContext.geodecoder.geocode({
26762 latLng: this.gMapContext.location
26763 }, function(results, status) {
26765 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26766 _this.gMapContext.locationName = results[0].formatted_address;
26767 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26769 _this.fireEvent('positionchanged', this, location);
26776 this.fireEvent('positionchanged', this, location);
26781 google.maps.event.trigger(this.gMapContext.map, "resize");
26783 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26785 this.fireEvent('resize', this);
26788 setPositionByLatLng: function(latitude, longitude)
26790 this.setPosition(new google.maps.LatLng(latitude, longitude));
26793 getCurrentPosition: function()
26796 latitude: this.gMapContext.location.lat(),
26797 longitude: this.gMapContext.location.lng()
26801 getAddressName: function()
26803 return this.gMapContext.locationName;
26806 getAddressComponents: function()
26808 return this.gMapContext.addressComponents;
26811 address_component_from_google_geocode: function(address_components)
26815 for (var i = 0; i < address_components.length; i++) {
26816 var component = address_components[i];
26817 if (component.types.indexOf("postal_code") >= 0) {
26818 result.postalCode = component.short_name;
26819 } else if (component.types.indexOf("street_number") >= 0) {
26820 result.streetNumber = component.short_name;
26821 } else if (component.types.indexOf("route") >= 0) {
26822 result.streetName = component.short_name;
26823 } else if (component.types.indexOf("neighborhood") >= 0) {
26824 result.city = component.short_name;
26825 } else if (component.types.indexOf("locality") >= 0) {
26826 result.city = component.short_name;
26827 } else if (component.types.indexOf("sublocality") >= 0) {
26828 result.district = component.short_name;
26829 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26830 result.stateOrProvince = component.short_name;
26831 } else if (component.types.indexOf("country") >= 0) {
26832 result.country = component.short_name;
26836 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26837 result.addressLine2 = "";
26841 setZoomLevel: function(zoom)
26843 this.gMapContext.map.setZoom(zoom);
26856 this.fireEvent('show', this);
26867 this.fireEvent('hide', this);
26872 Roo.apply(Roo.bootstrap.LocationPicker, {
26874 OverlayView : function(map, options)
26876 options = options || {};
26890 * @class Roo.bootstrap.Alert
26891 * @extends Roo.bootstrap.Component
26892 * Bootstrap Alert class
26893 * @cfg {String} title The title of alert
26894 * @cfg {String} html The content of alert
26895 * @cfg {String} weight ( success | info | warning | danger )
26896 * @cfg {String} faicon font-awesomeicon
26899 * Create a new alert
26900 * @param {Object} config The config object
26904 Roo.bootstrap.Alert = function(config){
26905 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26909 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26916 getAutoCreate : function()
26925 cls : 'roo-alert-icon'
26930 cls : 'roo-alert-title',
26935 cls : 'roo-alert-text',
26942 cfg.cn[0].cls += ' fa ' + this.faicon;
26946 cfg.cls += ' alert-' + this.weight;
26952 initEvents: function()
26954 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26957 setTitle : function(str)
26959 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26962 setText : function(str)
26964 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26967 setWeight : function(weight)
26970 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26973 this.weight = weight;
26975 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26978 setIcon : function(icon)
26981 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26984 this.faicon = icon;
26986 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27007 * @class Roo.bootstrap.UploadCropbox
27008 * @extends Roo.bootstrap.Component
27009 * Bootstrap UploadCropbox class
27010 * @cfg {String} emptyText show when image has been loaded
27011 * @cfg {String} rotateNotify show when image too small to rotate
27012 * @cfg {Number} errorTimeout default 3000
27013 * @cfg {Number} minWidth default 300
27014 * @cfg {Number} minHeight default 300
27015 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27016 * @cfg {Boolean} isDocument (true|false) default false
27017 * @cfg {String} url action url
27018 * @cfg {String} paramName default 'imageUpload'
27019 * @cfg {String} method default POST
27020 * @cfg {Boolean} loadMask (true|false) default true
27021 * @cfg {Boolean} loadingText default 'Loading...'
27024 * Create a new UploadCropbox
27025 * @param {Object} config The config object
27028 Roo.bootstrap.UploadCropbox = function(config){
27029 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27033 * @event beforeselectfile
27034 * Fire before select file
27035 * @param {Roo.bootstrap.UploadCropbox} this
27037 "beforeselectfile" : true,
27040 * Fire after initEvent
27041 * @param {Roo.bootstrap.UploadCropbox} this
27046 * Fire after initEvent
27047 * @param {Roo.bootstrap.UploadCropbox} this
27048 * @param {String} data
27053 * Fire when preparing the file data
27054 * @param {Roo.bootstrap.UploadCropbox} this
27055 * @param {Object} file
27060 * Fire when get exception
27061 * @param {Roo.bootstrap.UploadCropbox} this
27062 * @param {XMLHttpRequest} xhr
27064 "exception" : true,
27066 * @event beforeloadcanvas
27067 * Fire before load the canvas
27068 * @param {Roo.bootstrap.UploadCropbox} this
27069 * @param {String} src
27071 "beforeloadcanvas" : true,
27074 * Fire when trash image
27075 * @param {Roo.bootstrap.UploadCropbox} this
27080 * Fire when download the image
27081 * @param {Roo.bootstrap.UploadCropbox} this
27085 * @event footerbuttonclick
27086 * Fire when footerbuttonclick
27087 * @param {Roo.bootstrap.UploadCropbox} this
27088 * @param {String} type
27090 "footerbuttonclick" : true,
27094 * @param {Roo.bootstrap.UploadCropbox} this
27099 * Fire when rotate the image
27100 * @param {Roo.bootstrap.UploadCropbox} this
27101 * @param {String} pos
27106 * Fire when inspect the file
27107 * @param {Roo.bootstrap.UploadCropbox} this
27108 * @param {Object} file
27113 * Fire when xhr upload the file
27114 * @param {Roo.bootstrap.UploadCropbox} this
27115 * @param {Object} data
27120 * Fire when arrange the file data
27121 * @param {Roo.bootstrap.UploadCropbox} this
27122 * @param {Object} formData
27127 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27130 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27132 emptyText : 'Click to upload image',
27133 rotateNotify : 'Image is too small to rotate',
27134 errorTimeout : 3000,
27148 cropType : 'image/jpeg',
27150 canvasLoaded : false,
27151 isDocument : false,
27153 paramName : 'imageUpload',
27155 loadingText : 'Loading...',
27158 getAutoCreate : function()
27162 cls : 'roo-upload-cropbox',
27166 cls : 'roo-upload-cropbox-selector',
27171 cls : 'roo-upload-cropbox-body',
27172 style : 'cursor:pointer',
27176 cls : 'roo-upload-cropbox-preview'
27180 cls : 'roo-upload-cropbox-thumb'
27184 cls : 'roo-upload-cropbox-empty-notify',
27185 html : this.emptyText
27189 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27190 html : this.rotateNotify
27196 cls : 'roo-upload-cropbox-footer',
27199 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27209 onRender : function(ct, position)
27211 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27213 if (this.buttons.length) {
27215 Roo.each(this.buttons, function(bb) {
27217 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27219 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27225 this.maskEl = this.el;
27229 initEvents : function()
27231 this.urlAPI = (window.createObjectURL && window) ||
27232 (window.URL && URL.revokeObjectURL && URL) ||
27233 (window.webkitURL && webkitURL);
27235 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27236 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27238 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27239 this.selectorEl.hide();
27241 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27242 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27244 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27245 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27246 this.thumbEl.hide();
27248 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27249 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27251 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27252 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27253 this.errorEl.hide();
27255 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27256 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27257 this.footerEl.hide();
27259 this.setThumbBoxSize();
27265 this.fireEvent('initial', this);
27272 window.addEventListener("resize", function() { _this.resize(); } );
27274 this.bodyEl.on('click', this.beforeSelectFile, this);
27277 this.bodyEl.on('touchstart', this.onTouchStart, this);
27278 this.bodyEl.on('touchmove', this.onTouchMove, this);
27279 this.bodyEl.on('touchend', this.onTouchEnd, this);
27283 this.bodyEl.on('mousedown', this.onMouseDown, this);
27284 this.bodyEl.on('mousemove', this.onMouseMove, this);
27285 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27286 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27287 Roo.get(document).on('mouseup', this.onMouseUp, this);
27290 this.selectorEl.on('change', this.onFileSelected, this);
27296 this.baseScale = 1;
27298 this.baseRotate = 1;
27299 this.dragable = false;
27300 this.pinching = false;
27303 this.cropData = false;
27304 this.notifyEl.dom.innerHTML = this.emptyText;
27306 this.selectorEl.dom.value = '';
27310 resize : function()
27312 if(this.fireEvent('resize', this) != false){
27313 this.setThumbBoxPosition();
27314 this.setCanvasPosition();
27318 onFooterButtonClick : function(e, el, o, type)
27321 case 'rotate-left' :
27322 this.onRotateLeft(e);
27324 case 'rotate-right' :
27325 this.onRotateRight(e);
27328 this.beforeSelectFile(e);
27343 this.fireEvent('footerbuttonclick', this, type);
27346 beforeSelectFile : function(e)
27348 e.preventDefault();
27350 if(this.fireEvent('beforeselectfile', this) != false){
27351 this.selectorEl.dom.click();
27355 onFileSelected : function(e)
27357 e.preventDefault();
27359 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27363 var file = this.selectorEl.dom.files[0];
27365 if(this.fireEvent('inspect', this, file) != false){
27366 this.prepare(file);
27371 trash : function(e)
27373 this.fireEvent('trash', this);
27376 download : function(e)
27378 this.fireEvent('download', this);
27381 loadCanvas : function(src)
27383 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27387 this.imageEl = document.createElement('img');
27391 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27393 this.imageEl.src = src;
27397 onLoadCanvas : function()
27399 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27400 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27402 this.bodyEl.un('click', this.beforeSelectFile, this);
27404 this.notifyEl.hide();
27405 this.thumbEl.show();
27406 this.footerEl.show();
27408 this.baseRotateLevel();
27410 if(this.isDocument){
27411 this.setThumbBoxSize();
27414 this.setThumbBoxPosition();
27416 this.baseScaleLevel();
27422 this.canvasLoaded = true;
27425 this.maskEl.unmask();
27430 setCanvasPosition : function()
27432 if(!this.canvasEl){
27436 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27437 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27439 this.previewEl.setLeft(pw);
27440 this.previewEl.setTop(ph);
27444 onMouseDown : function(e)
27448 this.dragable = true;
27449 this.pinching = false;
27451 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27452 this.dragable = false;
27456 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27457 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27461 onMouseMove : function(e)
27465 if(!this.canvasLoaded){
27469 if (!this.dragable){
27473 var minX = Math.ceil(this.thumbEl.getLeft(true));
27474 var minY = Math.ceil(this.thumbEl.getTop(true));
27476 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27477 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27479 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27480 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27482 x = x - this.mouseX;
27483 y = y - this.mouseY;
27485 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27486 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27488 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27489 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27491 this.previewEl.setLeft(bgX);
27492 this.previewEl.setTop(bgY);
27494 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27495 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27498 onMouseUp : function(e)
27502 this.dragable = false;
27505 onMouseWheel : function(e)
27509 this.startScale = this.scale;
27511 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27513 if(!this.zoomable()){
27514 this.scale = this.startScale;
27523 zoomable : function()
27525 var minScale = this.thumbEl.getWidth() / this.minWidth;
27527 if(this.minWidth < this.minHeight){
27528 minScale = this.thumbEl.getHeight() / this.minHeight;
27531 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27532 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27536 (this.rotate == 0 || this.rotate == 180) &&
27538 width > this.imageEl.OriginWidth ||
27539 height > this.imageEl.OriginHeight ||
27540 (width < this.minWidth && height < this.minHeight)
27548 (this.rotate == 90 || this.rotate == 270) &&
27550 width > this.imageEl.OriginWidth ||
27551 height > this.imageEl.OriginHeight ||
27552 (width < this.minHeight && height < this.minWidth)
27559 !this.isDocument &&
27560 (this.rotate == 0 || this.rotate == 180) &&
27562 width < this.minWidth ||
27563 width > this.imageEl.OriginWidth ||
27564 height < this.minHeight ||
27565 height > this.imageEl.OriginHeight
27572 !this.isDocument &&
27573 (this.rotate == 90 || this.rotate == 270) &&
27575 width < this.minHeight ||
27576 width > this.imageEl.OriginWidth ||
27577 height < this.minWidth ||
27578 height > this.imageEl.OriginHeight
27588 onRotateLeft : function(e)
27590 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27592 var minScale = this.thumbEl.getWidth() / this.minWidth;
27594 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27595 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27597 this.startScale = this.scale;
27599 while (this.getScaleLevel() < minScale){
27601 this.scale = this.scale + 1;
27603 if(!this.zoomable()){
27608 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27609 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27614 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27621 this.scale = this.startScale;
27623 this.onRotateFail();
27628 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27630 if(this.isDocument){
27631 this.setThumbBoxSize();
27632 this.setThumbBoxPosition();
27633 this.setCanvasPosition();
27638 this.fireEvent('rotate', this, 'left');
27642 onRotateRight : function(e)
27644 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27646 var minScale = this.thumbEl.getWidth() / this.minWidth;
27648 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27649 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27651 this.startScale = this.scale;
27653 while (this.getScaleLevel() < minScale){
27655 this.scale = this.scale + 1;
27657 if(!this.zoomable()){
27662 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27663 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27668 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27675 this.scale = this.startScale;
27677 this.onRotateFail();
27682 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27684 if(this.isDocument){
27685 this.setThumbBoxSize();
27686 this.setThumbBoxPosition();
27687 this.setCanvasPosition();
27692 this.fireEvent('rotate', this, 'right');
27695 onRotateFail : function()
27697 this.errorEl.show(true);
27701 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27706 this.previewEl.dom.innerHTML = '';
27708 var canvasEl = document.createElement("canvas");
27710 var contextEl = canvasEl.getContext("2d");
27712 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27713 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27714 var center = this.imageEl.OriginWidth / 2;
27716 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27717 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27718 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27719 center = this.imageEl.OriginHeight / 2;
27722 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27724 contextEl.translate(center, center);
27725 contextEl.rotate(this.rotate * Math.PI / 180);
27727 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27729 this.canvasEl = document.createElement("canvas");
27731 this.contextEl = this.canvasEl.getContext("2d");
27733 switch (this.rotate) {
27736 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27737 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27739 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27744 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27745 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27747 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27748 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);
27752 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27757 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27758 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27760 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27761 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);
27765 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);
27770 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27771 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27773 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27774 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27778 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);
27785 this.previewEl.appendChild(this.canvasEl);
27787 this.setCanvasPosition();
27792 if(!this.canvasLoaded){
27796 var imageCanvas = document.createElement("canvas");
27798 var imageContext = imageCanvas.getContext("2d");
27800 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27801 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27803 var center = imageCanvas.width / 2;
27805 imageContext.translate(center, center);
27807 imageContext.rotate(this.rotate * Math.PI / 180);
27809 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27811 var canvas = document.createElement("canvas");
27813 var context = canvas.getContext("2d");
27815 canvas.width = this.minWidth;
27816 canvas.height = this.minHeight;
27818 switch (this.rotate) {
27821 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27822 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27824 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27825 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27827 var targetWidth = this.minWidth - 2 * x;
27828 var targetHeight = this.minHeight - 2 * y;
27832 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27833 scale = targetWidth / width;
27836 if(x > 0 && y == 0){
27837 scale = targetHeight / height;
27840 if(x > 0 && y > 0){
27841 scale = targetWidth / width;
27843 if(width < height){
27844 scale = targetHeight / height;
27848 context.scale(scale, scale);
27850 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27851 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27853 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27854 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27856 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27861 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27862 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27864 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27865 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27867 var targetWidth = this.minWidth - 2 * x;
27868 var targetHeight = this.minHeight - 2 * y;
27872 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27873 scale = targetWidth / width;
27876 if(x > 0 && y == 0){
27877 scale = targetHeight / height;
27880 if(x > 0 && y > 0){
27881 scale = targetWidth / width;
27883 if(width < height){
27884 scale = targetHeight / height;
27888 context.scale(scale, scale);
27890 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27891 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27893 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27894 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27896 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27898 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27903 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27904 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27906 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27907 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27909 var targetWidth = this.minWidth - 2 * x;
27910 var targetHeight = this.minHeight - 2 * y;
27914 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27915 scale = targetWidth / width;
27918 if(x > 0 && y == 0){
27919 scale = targetHeight / height;
27922 if(x > 0 && y > 0){
27923 scale = targetWidth / width;
27925 if(width < height){
27926 scale = targetHeight / height;
27930 context.scale(scale, scale);
27932 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27933 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27935 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27936 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27938 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27939 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27941 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27946 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27947 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27949 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27950 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27952 var targetWidth = this.minWidth - 2 * x;
27953 var targetHeight = this.minHeight - 2 * y;
27957 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27958 scale = targetWidth / width;
27961 if(x > 0 && y == 0){
27962 scale = targetHeight / height;
27965 if(x > 0 && y > 0){
27966 scale = targetWidth / width;
27968 if(width < height){
27969 scale = targetHeight / height;
27973 context.scale(scale, scale);
27975 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27976 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27978 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27979 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27981 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27983 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27990 this.cropData = canvas.toDataURL(this.cropType);
27992 if(this.fireEvent('crop', this, this.cropData) !== false){
27993 this.process(this.file, this.cropData);
28000 setThumbBoxSize : function()
28004 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28005 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28006 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28008 this.minWidth = width;
28009 this.minHeight = height;
28011 if(this.rotate == 90 || this.rotate == 270){
28012 this.minWidth = height;
28013 this.minHeight = width;
28018 width = Math.ceil(this.minWidth * height / this.minHeight);
28020 if(this.minWidth > this.minHeight){
28022 height = Math.ceil(this.minHeight * width / this.minWidth);
28025 this.thumbEl.setStyle({
28026 width : width + 'px',
28027 height : height + 'px'
28034 setThumbBoxPosition : function()
28036 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28037 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28039 this.thumbEl.setLeft(x);
28040 this.thumbEl.setTop(y);
28044 baseRotateLevel : function()
28046 this.baseRotate = 1;
28049 typeof(this.exif) != 'undefined' &&
28050 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28051 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28053 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28056 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28060 baseScaleLevel : function()
28064 if(this.isDocument){
28066 if(this.baseRotate == 6 || this.baseRotate == 8){
28068 height = this.thumbEl.getHeight();
28069 this.baseScale = height / this.imageEl.OriginWidth;
28071 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28072 width = this.thumbEl.getWidth();
28073 this.baseScale = width / this.imageEl.OriginHeight;
28079 height = this.thumbEl.getHeight();
28080 this.baseScale = height / this.imageEl.OriginHeight;
28082 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28083 width = this.thumbEl.getWidth();
28084 this.baseScale = width / this.imageEl.OriginWidth;
28090 if(this.baseRotate == 6 || this.baseRotate == 8){
28092 width = this.thumbEl.getHeight();
28093 this.baseScale = width / this.imageEl.OriginHeight;
28095 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28096 height = this.thumbEl.getWidth();
28097 this.baseScale = height / this.imageEl.OriginHeight;
28100 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28101 height = this.thumbEl.getWidth();
28102 this.baseScale = height / this.imageEl.OriginHeight;
28104 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28105 width = this.thumbEl.getHeight();
28106 this.baseScale = width / this.imageEl.OriginWidth;
28113 width = this.thumbEl.getWidth();
28114 this.baseScale = width / this.imageEl.OriginWidth;
28116 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28117 height = this.thumbEl.getHeight();
28118 this.baseScale = height / this.imageEl.OriginHeight;
28121 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28123 height = this.thumbEl.getHeight();
28124 this.baseScale = height / this.imageEl.OriginHeight;
28126 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28127 width = this.thumbEl.getWidth();
28128 this.baseScale = width / this.imageEl.OriginWidth;
28136 getScaleLevel : function()
28138 return this.baseScale * Math.pow(1.1, this.scale);
28141 onTouchStart : function(e)
28143 if(!this.canvasLoaded){
28144 this.beforeSelectFile(e);
28148 var touches = e.browserEvent.touches;
28154 if(touches.length == 1){
28155 this.onMouseDown(e);
28159 if(touches.length != 2){
28165 for(var i = 0, finger; finger = touches[i]; i++){
28166 coords.push(finger.pageX, finger.pageY);
28169 var x = Math.pow(coords[0] - coords[2], 2);
28170 var y = Math.pow(coords[1] - coords[3], 2);
28172 this.startDistance = Math.sqrt(x + y);
28174 this.startScale = this.scale;
28176 this.pinching = true;
28177 this.dragable = false;
28181 onTouchMove : function(e)
28183 if(!this.pinching && !this.dragable){
28187 var touches = e.browserEvent.touches;
28194 this.onMouseMove(e);
28200 for(var i = 0, finger; finger = touches[i]; i++){
28201 coords.push(finger.pageX, finger.pageY);
28204 var x = Math.pow(coords[0] - coords[2], 2);
28205 var y = Math.pow(coords[1] - coords[3], 2);
28207 this.endDistance = Math.sqrt(x + y);
28209 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28211 if(!this.zoomable()){
28212 this.scale = this.startScale;
28220 onTouchEnd : function(e)
28222 this.pinching = false;
28223 this.dragable = false;
28227 process : function(file, crop)
28230 this.maskEl.mask(this.loadingText);
28233 this.xhr = new XMLHttpRequest();
28235 file.xhr = this.xhr;
28237 this.xhr.open(this.method, this.url, true);
28240 "Accept": "application/json",
28241 "Cache-Control": "no-cache",
28242 "X-Requested-With": "XMLHttpRequest"
28245 for (var headerName in headers) {
28246 var headerValue = headers[headerName];
28248 this.xhr.setRequestHeader(headerName, headerValue);
28254 this.xhr.onload = function()
28256 _this.xhrOnLoad(_this.xhr);
28259 this.xhr.onerror = function()
28261 _this.xhrOnError(_this.xhr);
28264 var formData = new FormData();
28266 formData.append('returnHTML', 'NO');
28269 formData.append('crop', crop);
28272 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28273 formData.append(this.paramName, file, file.name);
28276 if(typeof(file.filename) != 'undefined'){
28277 formData.append('filename', file.filename);
28280 if(typeof(file.mimetype) != 'undefined'){
28281 formData.append('mimetype', file.mimetype);
28284 if(this.fireEvent('arrange', this, formData) != false){
28285 this.xhr.send(formData);
28289 xhrOnLoad : function(xhr)
28292 this.maskEl.unmask();
28295 if (xhr.readyState !== 4) {
28296 this.fireEvent('exception', this, xhr);
28300 var response = Roo.decode(xhr.responseText);
28302 if(!response.success){
28303 this.fireEvent('exception', this, xhr);
28307 var response = Roo.decode(xhr.responseText);
28309 this.fireEvent('upload', this, response);
28313 xhrOnError : function()
28316 this.maskEl.unmask();
28319 Roo.log('xhr on error');
28321 var response = Roo.decode(xhr.responseText);
28327 prepare : function(file)
28330 this.maskEl.mask(this.loadingText);
28336 if(typeof(file) === 'string'){
28337 this.loadCanvas(file);
28341 if(!file || !this.urlAPI){
28346 this.cropType = file.type;
28350 if(this.fireEvent('prepare', this, this.file) != false){
28352 var reader = new FileReader();
28354 reader.onload = function (e) {
28355 if (e.target.error) {
28356 Roo.log(e.target.error);
28360 var buffer = e.target.result,
28361 dataView = new DataView(buffer),
28363 maxOffset = dataView.byteLength - 4,
28367 if (dataView.getUint16(0) === 0xffd8) {
28368 while (offset < maxOffset) {
28369 markerBytes = dataView.getUint16(offset);
28371 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28372 markerLength = dataView.getUint16(offset + 2) + 2;
28373 if (offset + markerLength > dataView.byteLength) {
28374 Roo.log('Invalid meta data: Invalid segment size.');
28378 if(markerBytes == 0xffe1){
28379 _this.parseExifData(
28386 offset += markerLength;
28396 var url = _this.urlAPI.createObjectURL(_this.file);
28398 _this.loadCanvas(url);
28403 reader.readAsArrayBuffer(this.file);
28409 parseExifData : function(dataView, offset, length)
28411 var tiffOffset = offset + 10,
28415 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28416 // No Exif data, might be XMP data instead
28420 // Check for the ASCII code for "Exif" (0x45786966):
28421 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28422 // No Exif data, might be XMP data instead
28425 if (tiffOffset + 8 > dataView.byteLength) {
28426 Roo.log('Invalid Exif data: Invalid segment size.');
28429 // Check for the two null bytes:
28430 if (dataView.getUint16(offset + 8) !== 0x0000) {
28431 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28434 // Check the byte alignment:
28435 switch (dataView.getUint16(tiffOffset)) {
28437 littleEndian = true;
28440 littleEndian = false;
28443 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28446 // Check for the TIFF tag marker (0x002A):
28447 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28448 Roo.log('Invalid Exif data: Missing TIFF marker.');
28451 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28452 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28454 this.parseExifTags(
28457 tiffOffset + dirOffset,
28462 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28467 if (dirOffset + 6 > dataView.byteLength) {
28468 Roo.log('Invalid Exif data: Invalid directory offset.');
28471 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28472 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28473 if (dirEndOffset + 4 > dataView.byteLength) {
28474 Roo.log('Invalid Exif data: Invalid directory size.');
28477 for (i = 0; i < tagsNumber; i += 1) {
28481 dirOffset + 2 + 12 * i, // tag offset
28485 // Return the offset to the next directory:
28486 return dataView.getUint32(dirEndOffset, littleEndian);
28489 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28491 var tag = dataView.getUint16(offset, littleEndian);
28493 this.exif[tag] = this.getExifValue(
28497 dataView.getUint16(offset + 2, littleEndian), // tag type
28498 dataView.getUint32(offset + 4, littleEndian), // tag length
28503 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28505 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28514 Roo.log('Invalid Exif data: Invalid tag type.');
28518 tagSize = tagType.size * length;
28519 // Determine if the value is contained in the dataOffset bytes,
28520 // or if the value at the dataOffset is a pointer to the actual data:
28521 dataOffset = tagSize > 4 ?
28522 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28523 if (dataOffset + tagSize > dataView.byteLength) {
28524 Roo.log('Invalid Exif data: Invalid data offset.');
28527 if (length === 1) {
28528 return tagType.getValue(dataView, dataOffset, littleEndian);
28531 for (i = 0; i < length; i += 1) {
28532 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28535 if (tagType.ascii) {
28537 // Concatenate the chars:
28538 for (i = 0; i < values.length; i += 1) {
28540 // Ignore the terminating NULL byte(s):
28541 if (c === '\u0000') {
28553 Roo.apply(Roo.bootstrap.UploadCropbox, {
28555 'Orientation': 0x0112
28559 1: 0, //'top-left',
28561 3: 180, //'bottom-right',
28562 // 4: 'bottom-left',
28564 6: 90, //'right-top',
28565 // 7: 'right-bottom',
28566 8: 270 //'left-bottom'
28570 // byte, 8-bit unsigned int:
28572 getValue: function (dataView, dataOffset) {
28573 return dataView.getUint8(dataOffset);
28577 // ascii, 8-bit byte:
28579 getValue: function (dataView, dataOffset) {
28580 return String.fromCharCode(dataView.getUint8(dataOffset));
28585 // short, 16 bit int:
28587 getValue: function (dataView, dataOffset, littleEndian) {
28588 return dataView.getUint16(dataOffset, littleEndian);
28592 // long, 32 bit int:
28594 getValue: function (dataView, dataOffset, littleEndian) {
28595 return dataView.getUint32(dataOffset, littleEndian);
28599 // rational = two long values, first is numerator, second is denominator:
28601 getValue: function (dataView, dataOffset, littleEndian) {
28602 return dataView.getUint32(dataOffset, littleEndian) /
28603 dataView.getUint32(dataOffset + 4, littleEndian);
28607 // slong, 32 bit signed int:
28609 getValue: function (dataView, dataOffset, littleEndian) {
28610 return dataView.getInt32(dataOffset, littleEndian);
28614 // srational, two slongs, first is numerator, second is denominator:
28616 getValue: function (dataView, dataOffset, littleEndian) {
28617 return dataView.getInt32(dataOffset, littleEndian) /
28618 dataView.getInt32(dataOffset + 4, littleEndian);
28628 cls : 'btn-group roo-upload-cropbox-rotate-left',
28629 action : 'rotate-left',
28633 cls : 'btn btn-default',
28634 html : '<i class="fa fa-undo"></i>'
28640 cls : 'btn-group roo-upload-cropbox-picture',
28641 action : 'picture',
28645 cls : 'btn btn-default',
28646 html : '<i class="fa fa-picture-o"></i>'
28652 cls : 'btn-group roo-upload-cropbox-rotate-right',
28653 action : 'rotate-right',
28657 cls : 'btn btn-default',
28658 html : '<i class="fa fa-repeat"></i>'
28666 cls : 'btn-group roo-upload-cropbox-rotate-left',
28667 action : 'rotate-left',
28671 cls : 'btn btn-default',
28672 html : '<i class="fa fa-undo"></i>'
28678 cls : 'btn-group roo-upload-cropbox-download',
28679 action : 'download',
28683 cls : 'btn btn-default',
28684 html : '<i class="fa fa-download"></i>'
28690 cls : 'btn-group roo-upload-cropbox-crop',
28695 cls : 'btn btn-default',
28696 html : '<i class="fa fa-crop"></i>'
28702 cls : 'btn-group roo-upload-cropbox-trash',
28707 cls : 'btn btn-default',
28708 html : '<i class="fa fa-trash"></i>'
28714 cls : 'btn-group roo-upload-cropbox-rotate-right',
28715 action : 'rotate-right',
28719 cls : 'btn btn-default',
28720 html : '<i class="fa fa-repeat"></i>'
28728 cls : 'btn-group roo-upload-cropbox-rotate-left',
28729 action : 'rotate-left',
28733 cls : 'btn btn-default',
28734 html : '<i class="fa fa-undo"></i>'
28740 cls : 'btn-group roo-upload-cropbox-rotate-right',
28741 action : 'rotate-right',
28745 cls : 'btn btn-default',
28746 html : '<i class="fa fa-repeat"></i>'
28759 * @class Roo.bootstrap.DocumentManager
28760 * @extends Roo.bootstrap.Component
28761 * Bootstrap DocumentManager class
28762 * @cfg {String} paramName default 'imageUpload'
28763 * @cfg {String} toolTipName default 'filename'
28764 * @cfg {String} method default POST
28765 * @cfg {String} url action url
28766 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28767 * @cfg {Boolean} multiple multiple upload default true
28768 * @cfg {Number} thumbSize default 300
28769 * @cfg {String} fieldLabel
28770 * @cfg {Number} labelWidth default 4
28771 * @cfg {String} labelAlign (left|top) default left
28772 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28773 * @cfg {Number} labellg set the width of label (1-12)
28774 * @cfg {Number} labelmd set the width of label (1-12)
28775 * @cfg {Number} labelsm set the width of label (1-12)
28776 * @cfg {Number} labelxs set the width of label (1-12)
28779 * Create a new DocumentManager
28780 * @param {Object} config The config object
28783 Roo.bootstrap.DocumentManager = function(config){
28784 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28787 this.delegates = [];
28792 * Fire when initial the DocumentManager
28793 * @param {Roo.bootstrap.DocumentManager} this
28798 * inspect selected file
28799 * @param {Roo.bootstrap.DocumentManager} this
28800 * @param {File} file
28805 * Fire when xhr load exception
28806 * @param {Roo.bootstrap.DocumentManager} this
28807 * @param {XMLHttpRequest} xhr
28809 "exception" : true,
28811 * @event afterupload
28812 * Fire when xhr load exception
28813 * @param {Roo.bootstrap.DocumentManager} this
28814 * @param {XMLHttpRequest} xhr
28816 "afterupload" : true,
28819 * prepare the form data
28820 * @param {Roo.bootstrap.DocumentManager} this
28821 * @param {Object} formData
28826 * Fire when remove the file
28827 * @param {Roo.bootstrap.DocumentManager} this
28828 * @param {Object} file
28833 * Fire after refresh the file
28834 * @param {Roo.bootstrap.DocumentManager} this
28839 * Fire after click the image
28840 * @param {Roo.bootstrap.DocumentManager} this
28841 * @param {Object} file
28846 * Fire when upload a image and editable set to true
28847 * @param {Roo.bootstrap.DocumentManager} this
28848 * @param {Object} file
28852 * @event beforeselectfile
28853 * Fire before select file
28854 * @param {Roo.bootstrap.DocumentManager} this
28856 "beforeselectfile" : true,
28859 * Fire before process file
28860 * @param {Roo.bootstrap.DocumentManager} this
28861 * @param {Object} file
28865 * @event previewrendered
28866 * Fire when preview rendered
28867 * @param {Roo.bootstrap.DocumentManager} this
28868 * @param {Object} file
28870 "previewrendered" : true,
28873 "previewResize" : true
28878 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28887 paramName : 'imageUpload',
28888 toolTipName : 'filename',
28891 labelAlign : 'left',
28901 getAutoCreate : function()
28903 var managerWidget = {
28905 cls : 'roo-document-manager',
28909 cls : 'roo-document-manager-selector',
28914 cls : 'roo-document-manager-uploader',
28918 cls : 'roo-document-manager-upload-btn',
28919 html : '<i class="fa fa-plus"></i>'
28930 cls : 'column col-md-12',
28935 if(this.fieldLabel.length){
28940 cls : 'column col-md-12',
28941 html : this.fieldLabel
28945 cls : 'column col-md-12',
28950 if(this.labelAlign == 'left'){
28955 html : this.fieldLabel
28964 if(this.labelWidth > 12){
28965 content[0].style = "width: " + this.labelWidth + 'px';
28968 if(this.labelWidth < 13 && this.labelmd == 0){
28969 this.labelmd = this.labelWidth;
28972 if(this.labellg > 0){
28973 content[0].cls += ' col-lg-' + this.labellg;
28974 content[1].cls += ' col-lg-' + (12 - this.labellg);
28977 if(this.labelmd > 0){
28978 content[0].cls += ' col-md-' + this.labelmd;
28979 content[1].cls += ' col-md-' + (12 - this.labelmd);
28982 if(this.labelsm > 0){
28983 content[0].cls += ' col-sm-' + this.labelsm;
28984 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28987 if(this.labelxs > 0){
28988 content[0].cls += ' col-xs-' + this.labelxs;
28989 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28997 cls : 'row clearfix',
29005 initEvents : function()
29007 this.managerEl = this.el.select('.roo-document-manager', true).first();
29008 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29010 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29011 this.selectorEl.hide();
29014 this.selectorEl.attr('multiple', 'multiple');
29017 this.selectorEl.on('change', this.onFileSelected, this);
29019 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29020 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29022 this.uploader.on('click', this.onUploaderClick, this);
29024 this.renderProgressDialog();
29028 window.addEventListener("resize", function() { _this.refresh(); } );
29030 this.fireEvent('initial', this);
29033 renderProgressDialog : function()
29037 this.progressDialog = new Roo.bootstrap.Modal({
29038 cls : 'roo-document-manager-progress-dialog',
29039 allow_close : false,
29049 btnclick : function() {
29050 _this.uploadCancel();
29056 this.progressDialog.render(Roo.get(document.body));
29058 this.progress = new Roo.bootstrap.Progress({
29059 cls : 'roo-document-manager-progress',
29064 this.progress.render(this.progressDialog.getChildContainer());
29066 this.progressBar = new Roo.bootstrap.ProgressBar({
29067 cls : 'roo-document-manager-progress-bar',
29070 aria_valuemax : 12,
29074 this.progressBar.render(this.progress.getChildContainer());
29077 onUploaderClick : function(e)
29079 e.preventDefault();
29081 if(this.fireEvent('beforeselectfile', this) != false){
29082 this.selectorEl.dom.click();
29087 onFileSelected : function(e)
29089 e.preventDefault();
29091 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29095 Roo.each(this.selectorEl.dom.files, function(file){
29096 if(this.fireEvent('inspect', this, file) != false){
29097 this.files.push(file);
29107 this.selectorEl.dom.value = '';
29109 if(!this.files || !this.files.length){
29113 if(this.boxes > 0 && this.files.length > this.boxes){
29114 this.files = this.files.slice(0, this.boxes);
29117 this.uploader.show();
29119 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29120 this.uploader.hide();
29129 Roo.each(this.files, function(file){
29131 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29132 var f = this.renderPreview(file);
29137 if(file.type.indexOf('image') != -1){
29138 this.delegates.push(
29140 _this.process(file);
29141 }).createDelegate(this)
29149 _this.process(file);
29150 }).createDelegate(this)
29155 this.files = files;
29157 this.delegates = this.delegates.concat(docs);
29159 if(!this.delegates.length){
29164 this.progressBar.aria_valuemax = this.delegates.length;
29171 arrange : function()
29173 if(!this.delegates.length){
29174 this.progressDialog.hide();
29179 var delegate = this.delegates.shift();
29181 this.progressDialog.show();
29183 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29185 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29190 refresh : function()
29192 this.uploader.show();
29194 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29195 this.uploader.hide();
29198 Roo.isTouch ? this.closable(false) : this.closable(true);
29200 this.fireEvent('refresh', this);
29203 onRemove : function(e, el, o)
29205 e.preventDefault();
29207 this.fireEvent('remove', this, o);
29211 remove : function(o)
29215 Roo.each(this.files, function(file){
29216 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29225 this.files = files;
29232 Roo.each(this.files, function(file){
29237 file.target.remove();
29246 onClick : function(e, el, o)
29248 e.preventDefault();
29250 this.fireEvent('click', this, o);
29254 closable : function(closable)
29256 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29258 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29270 xhrOnLoad : function(xhr)
29272 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29276 if (xhr.readyState !== 4) {
29278 this.fireEvent('exception', this, xhr);
29282 var response = Roo.decode(xhr.responseText);
29284 if(!response.success){
29286 this.fireEvent('exception', this, xhr);
29290 var file = this.renderPreview(response.data);
29292 this.files.push(file);
29296 this.fireEvent('afterupload', this, xhr);
29300 xhrOnError : function(xhr)
29302 Roo.log('xhr on error');
29304 var response = Roo.decode(xhr.responseText);
29311 process : function(file)
29313 if(this.fireEvent('process', this, file) !== false){
29314 if(this.editable && file.type.indexOf('image') != -1){
29315 this.fireEvent('edit', this, file);
29319 this.uploadStart(file, false);
29326 uploadStart : function(file, crop)
29328 this.xhr = new XMLHttpRequest();
29330 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29335 file.xhr = this.xhr;
29337 this.managerEl.createChild({
29339 cls : 'roo-document-manager-loading',
29343 tooltip : file.name,
29344 cls : 'roo-document-manager-thumb',
29345 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29351 this.xhr.open(this.method, this.url, true);
29354 "Accept": "application/json",
29355 "Cache-Control": "no-cache",
29356 "X-Requested-With": "XMLHttpRequest"
29359 for (var headerName in headers) {
29360 var headerValue = headers[headerName];
29362 this.xhr.setRequestHeader(headerName, headerValue);
29368 this.xhr.onload = function()
29370 _this.xhrOnLoad(_this.xhr);
29373 this.xhr.onerror = function()
29375 _this.xhrOnError(_this.xhr);
29378 var formData = new FormData();
29380 formData.append('returnHTML', 'NO');
29383 formData.append('crop', crop);
29386 formData.append(this.paramName, file, file.name);
29393 if(this.fireEvent('prepare', this, formData, options) != false){
29395 if(options.manually){
29399 this.xhr.send(formData);
29403 this.uploadCancel();
29406 uploadCancel : function()
29412 this.delegates = [];
29414 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29421 renderPreview : function(file)
29423 if(typeof(file.target) != 'undefined' && file.target){
29427 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29429 var previewEl = this.managerEl.createChild({
29431 cls : 'roo-document-manager-preview',
29435 tooltip : file[this.toolTipName],
29436 cls : 'roo-document-manager-thumb',
29437 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29442 html : '<i class="fa fa-times-circle"></i>'
29447 var close = previewEl.select('button.close', true).first();
29449 close.on('click', this.onRemove, this, file);
29451 file.target = previewEl;
29453 var image = previewEl.select('img', true).first();
29457 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29459 image.on('click', this.onClick, this, file);
29461 this.fireEvent('previewrendered', this, file);
29467 onPreviewLoad : function(file, image)
29469 if(typeof(file.target) == 'undefined' || !file.target){
29473 var width = image.dom.naturalWidth || image.dom.width;
29474 var height = image.dom.naturalHeight || image.dom.height;
29476 if(!this.previewResize) {
29480 if(width > height){
29481 file.target.addClass('wide');
29485 file.target.addClass('tall');
29490 uploadFromSource : function(file, crop)
29492 this.xhr = new XMLHttpRequest();
29494 this.managerEl.createChild({
29496 cls : 'roo-document-manager-loading',
29500 tooltip : file.name,
29501 cls : 'roo-document-manager-thumb',
29502 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29508 this.xhr.open(this.method, this.url, true);
29511 "Accept": "application/json",
29512 "Cache-Control": "no-cache",
29513 "X-Requested-With": "XMLHttpRequest"
29516 for (var headerName in headers) {
29517 var headerValue = headers[headerName];
29519 this.xhr.setRequestHeader(headerName, headerValue);
29525 this.xhr.onload = function()
29527 _this.xhrOnLoad(_this.xhr);
29530 this.xhr.onerror = function()
29532 _this.xhrOnError(_this.xhr);
29535 var formData = new FormData();
29537 formData.append('returnHTML', 'NO');
29539 formData.append('crop', crop);
29541 if(typeof(file.filename) != 'undefined'){
29542 formData.append('filename', file.filename);
29545 if(typeof(file.mimetype) != 'undefined'){
29546 formData.append('mimetype', file.mimetype);
29551 if(this.fireEvent('prepare', this, formData) != false){
29552 this.xhr.send(formData);
29562 * @class Roo.bootstrap.DocumentViewer
29563 * @extends Roo.bootstrap.Component
29564 * Bootstrap DocumentViewer class
29565 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29566 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29569 * Create a new DocumentViewer
29570 * @param {Object} config The config object
29573 Roo.bootstrap.DocumentViewer = function(config){
29574 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29579 * Fire after initEvent
29580 * @param {Roo.bootstrap.DocumentViewer} this
29586 * @param {Roo.bootstrap.DocumentViewer} this
29591 * Fire after download button
29592 * @param {Roo.bootstrap.DocumentViewer} this
29597 * Fire after trash button
29598 * @param {Roo.bootstrap.DocumentViewer} this
29605 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29607 showDownload : true,
29611 getAutoCreate : function()
29615 cls : 'roo-document-viewer',
29619 cls : 'roo-document-viewer-body',
29623 cls : 'roo-document-viewer-thumb',
29627 cls : 'roo-document-viewer-image'
29635 cls : 'roo-document-viewer-footer',
29638 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29642 cls : 'btn-group roo-document-viewer-download',
29646 cls : 'btn btn-default',
29647 html : '<i class="fa fa-download"></i>'
29653 cls : 'btn-group roo-document-viewer-trash',
29657 cls : 'btn btn-default',
29658 html : '<i class="fa fa-trash"></i>'
29671 initEvents : function()
29673 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29674 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29676 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29677 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29679 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29680 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29682 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29683 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29685 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29686 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29688 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29689 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29691 this.bodyEl.on('click', this.onClick, this);
29692 this.downloadBtn.on('click', this.onDownload, this);
29693 this.trashBtn.on('click', this.onTrash, this);
29695 this.downloadBtn.hide();
29696 this.trashBtn.hide();
29698 if(this.showDownload){
29699 this.downloadBtn.show();
29702 if(this.showTrash){
29703 this.trashBtn.show();
29706 if(!this.showDownload && !this.showTrash) {
29707 this.footerEl.hide();
29712 initial : function()
29714 this.fireEvent('initial', this);
29718 onClick : function(e)
29720 e.preventDefault();
29722 this.fireEvent('click', this);
29725 onDownload : function(e)
29727 e.preventDefault();
29729 this.fireEvent('download', this);
29732 onTrash : function(e)
29734 e.preventDefault();
29736 this.fireEvent('trash', this);
29748 * @class Roo.bootstrap.NavProgressBar
29749 * @extends Roo.bootstrap.Component
29750 * Bootstrap NavProgressBar class
29753 * Create a new nav progress bar
29754 * @param {Object} config The config object
29757 Roo.bootstrap.NavProgressBar = function(config){
29758 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29760 this.bullets = this.bullets || [];
29762 // Roo.bootstrap.NavProgressBar.register(this);
29766 * Fires when the active item changes
29767 * @param {Roo.bootstrap.NavProgressBar} this
29768 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29769 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29776 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29781 getAutoCreate : function()
29783 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29787 cls : 'roo-navigation-bar-group',
29791 cls : 'roo-navigation-top-bar'
29795 cls : 'roo-navigation-bullets-bar',
29799 cls : 'roo-navigation-bar'
29806 cls : 'roo-navigation-bottom-bar'
29816 initEvents: function()
29821 onRender : function(ct, position)
29823 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29825 if(this.bullets.length){
29826 Roo.each(this.bullets, function(b){
29835 addItem : function(cfg)
29837 var item = new Roo.bootstrap.NavProgressItem(cfg);
29839 item.parentId = this.id;
29840 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29843 var top = new Roo.bootstrap.Element({
29845 cls : 'roo-navigation-bar-text'
29848 var bottom = new Roo.bootstrap.Element({
29850 cls : 'roo-navigation-bar-text'
29853 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29854 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29856 var topText = new Roo.bootstrap.Element({
29858 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29861 var bottomText = new Roo.bootstrap.Element({
29863 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29866 topText.onRender(top.el, null);
29867 bottomText.onRender(bottom.el, null);
29870 item.bottomEl = bottom;
29873 this.barItems.push(item);
29878 getActive : function()
29880 var active = false;
29882 Roo.each(this.barItems, function(v){
29884 if (!v.isActive()) {
29896 setActiveItem : function(item)
29900 Roo.each(this.barItems, function(v){
29901 if (v.rid == item.rid) {
29905 if (v.isActive()) {
29906 v.setActive(false);
29911 item.setActive(true);
29913 this.fireEvent('changed', this, item, prev);
29916 getBarItem: function(rid)
29920 Roo.each(this.barItems, function(e) {
29921 if (e.rid != rid) {
29932 indexOfItem : function(item)
29936 Roo.each(this.barItems, function(v, i){
29938 if (v.rid != item.rid) {
29949 setActiveNext : function()
29951 var i = this.indexOfItem(this.getActive());
29953 if (i > this.barItems.length) {
29957 this.setActiveItem(this.barItems[i+1]);
29960 setActivePrev : function()
29962 var i = this.indexOfItem(this.getActive());
29968 this.setActiveItem(this.barItems[i-1]);
29971 format : function()
29973 if(!this.barItems.length){
29977 var width = 100 / this.barItems.length;
29979 Roo.each(this.barItems, function(i){
29980 i.el.setStyle('width', width + '%');
29981 i.topEl.el.setStyle('width', width + '%');
29982 i.bottomEl.el.setStyle('width', width + '%');
29991 * Nav Progress Item
29996 * @class Roo.bootstrap.NavProgressItem
29997 * @extends Roo.bootstrap.Component
29998 * Bootstrap NavProgressItem class
29999 * @cfg {String} rid the reference id
30000 * @cfg {Boolean} active (true|false) Is item active default false
30001 * @cfg {Boolean} disabled (true|false) Is item active default false
30002 * @cfg {String} html
30003 * @cfg {String} position (top|bottom) text position default bottom
30004 * @cfg {String} icon show icon instead of number
30007 * Create a new NavProgressItem
30008 * @param {Object} config The config object
30010 Roo.bootstrap.NavProgressItem = function(config){
30011 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30016 * The raw click event for the entire grid.
30017 * @param {Roo.bootstrap.NavProgressItem} this
30018 * @param {Roo.EventObject} e
30025 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30031 position : 'bottom',
30034 getAutoCreate : function()
30036 var iconCls = 'roo-navigation-bar-item-icon';
30038 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30042 cls: 'roo-navigation-bar-item',
30052 cfg.cls += ' active';
30055 cfg.cls += ' disabled';
30061 disable : function()
30063 this.setDisabled(true);
30066 enable : function()
30068 this.setDisabled(false);
30071 initEvents: function()
30073 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30075 this.iconEl.on('click', this.onClick, this);
30078 onClick : function(e)
30080 e.preventDefault();
30086 if(this.fireEvent('click', this, e) === false){
30090 this.parent().setActiveItem(this);
30093 isActive: function ()
30095 return this.active;
30098 setActive : function(state)
30100 if(this.active == state){
30104 this.active = state;
30107 this.el.addClass('active');
30111 this.el.removeClass('active');
30116 setDisabled : function(state)
30118 if(this.disabled == state){
30122 this.disabled = state;
30125 this.el.addClass('disabled');
30129 this.el.removeClass('disabled');
30132 tooltipEl : function()
30134 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30147 * @class Roo.bootstrap.FieldLabel
30148 * @extends Roo.bootstrap.Component
30149 * Bootstrap FieldLabel class
30150 * @cfg {String} html contents of the element
30151 * @cfg {String} tag tag of the element default label
30152 * @cfg {String} cls class of the element
30153 * @cfg {String} target label target
30154 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30155 * @cfg {String} invalidClass default "text-warning"
30156 * @cfg {String} validClass default "text-success"
30157 * @cfg {String} iconTooltip default "This field is required"
30158 * @cfg {String} indicatorpos (left|right) default left
30161 * Create a new FieldLabel
30162 * @param {Object} config The config object
30165 Roo.bootstrap.FieldLabel = function(config){
30166 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30171 * Fires after the field has been marked as invalid.
30172 * @param {Roo.form.FieldLabel} this
30173 * @param {String} msg The validation message
30178 * Fires after the field has been validated with no errors.
30179 * @param {Roo.form.FieldLabel} this
30185 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30192 invalidClass : 'has-warning',
30193 validClass : 'has-success',
30194 iconTooltip : 'This field is required',
30195 indicatorpos : 'left',
30197 getAutoCreate : function(){
30200 if (!this.allowBlank) {
30206 cls : 'roo-bootstrap-field-label ' + this.cls,
30211 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30212 tooltip : this.iconTooltip
30221 if(this.indicatorpos == 'right'){
30224 cls : 'roo-bootstrap-field-label ' + this.cls,
30233 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30234 tooltip : this.iconTooltip
30243 initEvents: function()
30245 Roo.bootstrap.Element.superclass.initEvents.call(this);
30247 this.indicator = this.indicatorEl();
30249 if(this.indicator){
30250 this.indicator.removeClass('visible');
30251 this.indicator.addClass('invisible');
30254 Roo.bootstrap.FieldLabel.register(this);
30257 indicatorEl : function()
30259 var indicator = this.el.select('i.roo-required-indicator',true).first();
30270 * Mark this field as valid
30272 markValid : function()
30274 if(this.indicator){
30275 this.indicator.removeClass('visible');
30276 this.indicator.addClass('invisible');
30279 this.el.removeClass(this.invalidClass);
30281 this.el.addClass(this.validClass);
30283 this.fireEvent('valid', this);
30287 * Mark this field as invalid
30288 * @param {String} msg The validation message
30290 markInvalid : function(msg)
30292 if(this.indicator){
30293 this.indicator.removeClass('invisible');
30294 this.indicator.addClass('visible');
30297 this.el.removeClass(this.validClass);
30299 this.el.addClass(this.invalidClass);
30301 this.fireEvent('invalid', this, msg);
30307 Roo.apply(Roo.bootstrap.FieldLabel, {
30312 * register a FieldLabel Group
30313 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30315 register : function(label)
30317 if(this.groups.hasOwnProperty(label.target)){
30321 this.groups[label.target] = label;
30325 * fetch a FieldLabel Group based on the target
30326 * @param {string} target
30327 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30329 get: function(target) {
30330 if (typeof(this.groups[target]) == 'undefined') {
30334 return this.groups[target] ;
30343 * page DateSplitField.
30349 * @class Roo.bootstrap.DateSplitField
30350 * @extends Roo.bootstrap.Component
30351 * Bootstrap DateSplitField class
30352 * @cfg {string} fieldLabel - the label associated
30353 * @cfg {Number} labelWidth set the width of label (0-12)
30354 * @cfg {String} labelAlign (top|left)
30355 * @cfg {Boolean} dayAllowBlank (true|false) default false
30356 * @cfg {Boolean} monthAllowBlank (true|false) default false
30357 * @cfg {Boolean} yearAllowBlank (true|false) default false
30358 * @cfg {string} dayPlaceholder
30359 * @cfg {string} monthPlaceholder
30360 * @cfg {string} yearPlaceholder
30361 * @cfg {string} dayFormat default 'd'
30362 * @cfg {string} monthFormat default 'm'
30363 * @cfg {string} yearFormat default 'Y'
30364 * @cfg {Number} labellg set the width of label (1-12)
30365 * @cfg {Number} labelmd set the width of label (1-12)
30366 * @cfg {Number} labelsm set the width of label (1-12)
30367 * @cfg {Number} labelxs set the width of label (1-12)
30371 * Create a new DateSplitField
30372 * @param {Object} config The config object
30375 Roo.bootstrap.DateSplitField = function(config){
30376 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30382 * getting the data of years
30383 * @param {Roo.bootstrap.DateSplitField} this
30384 * @param {Object} years
30389 * getting the data of days
30390 * @param {Roo.bootstrap.DateSplitField} this
30391 * @param {Object} days
30396 * Fires after the field has been marked as invalid.
30397 * @param {Roo.form.Field} this
30398 * @param {String} msg The validation message
30403 * Fires after the field has been validated with no errors.
30404 * @param {Roo.form.Field} this
30410 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30413 labelAlign : 'top',
30415 dayAllowBlank : false,
30416 monthAllowBlank : false,
30417 yearAllowBlank : false,
30418 dayPlaceholder : '',
30419 monthPlaceholder : '',
30420 yearPlaceholder : '',
30424 isFormField : true,
30430 getAutoCreate : function()
30434 cls : 'row roo-date-split-field-group',
30439 cls : 'form-hidden-field roo-date-split-field-group-value',
30445 var labelCls = 'col-md-12';
30446 var contentCls = 'col-md-4';
30448 if(this.fieldLabel){
30452 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30456 html : this.fieldLabel
30461 if(this.labelAlign == 'left'){
30463 if(this.labelWidth > 12){
30464 label.style = "width: " + this.labelWidth + 'px';
30467 if(this.labelWidth < 13 && this.labelmd == 0){
30468 this.labelmd = this.labelWidth;
30471 if(this.labellg > 0){
30472 labelCls = ' col-lg-' + this.labellg;
30473 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30476 if(this.labelmd > 0){
30477 labelCls = ' col-md-' + this.labelmd;
30478 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30481 if(this.labelsm > 0){
30482 labelCls = ' col-sm-' + this.labelsm;
30483 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30486 if(this.labelxs > 0){
30487 labelCls = ' col-xs-' + this.labelxs;
30488 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30492 label.cls += ' ' + labelCls;
30494 cfg.cn.push(label);
30497 Roo.each(['day', 'month', 'year'], function(t){
30500 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30507 inputEl: function ()
30509 return this.el.select('.roo-date-split-field-group-value', true).first();
30512 onRender : function(ct, position)
30516 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30518 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30520 this.dayField = new Roo.bootstrap.ComboBox({
30521 allowBlank : this.dayAllowBlank,
30522 alwaysQuery : true,
30523 displayField : 'value',
30526 forceSelection : true,
30528 placeholder : this.dayPlaceholder,
30529 selectOnFocus : true,
30530 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30531 triggerAction : 'all',
30533 valueField : 'value',
30534 store : new Roo.data.SimpleStore({
30535 data : (function() {
30537 _this.fireEvent('days', _this, days);
30540 fields : [ 'value' ]
30543 select : function (_self, record, index)
30545 _this.setValue(_this.getValue());
30550 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30552 this.monthField = new Roo.bootstrap.MonthField({
30553 after : '<i class=\"fa fa-calendar\"></i>',
30554 allowBlank : this.monthAllowBlank,
30555 placeholder : this.monthPlaceholder,
30558 render : function (_self)
30560 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30561 e.preventDefault();
30565 select : function (_self, oldvalue, newvalue)
30567 _this.setValue(_this.getValue());
30572 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30574 this.yearField = new Roo.bootstrap.ComboBox({
30575 allowBlank : this.yearAllowBlank,
30576 alwaysQuery : true,
30577 displayField : 'value',
30580 forceSelection : true,
30582 placeholder : this.yearPlaceholder,
30583 selectOnFocus : true,
30584 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30585 triggerAction : 'all',
30587 valueField : 'value',
30588 store : new Roo.data.SimpleStore({
30589 data : (function() {
30591 _this.fireEvent('years', _this, years);
30594 fields : [ 'value' ]
30597 select : function (_self, record, index)
30599 _this.setValue(_this.getValue());
30604 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30607 setValue : function(v, format)
30609 this.inputEl.dom.value = v;
30611 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30613 var d = Date.parseDate(v, f);
30620 this.setDay(d.format(this.dayFormat));
30621 this.setMonth(d.format(this.monthFormat));
30622 this.setYear(d.format(this.yearFormat));
30629 setDay : function(v)
30631 this.dayField.setValue(v);
30632 this.inputEl.dom.value = this.getValue();
30637 setMonth : function(v)
30639 this.monthField.setValue(v, true);
30640 this.inputEl.dom.value = this.getValue();
30645 setYear : function(v)
30647 this.yearField.setValue(v);
30648 this.inputEl.dom.value = this.getValue();
30653 getDay : function()
30655 return this.dayField.getValue();
30658 getMonth : function()
30660 return this.monthField.getValue();
30663 getYear : function()
30665 return this.yearField.getValue();
30668 getValue : function()
30670 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30672 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30682 this.inputEl.dom.value = '';
30687 validate : function()
30689 var d = this.dayField.validate();
30690 var m = this.monthField.validate();
30691 var y = this.yearField.validate();
30696 (!this.dayAllowBlank && !d) ||
30697 (!this.monthAllowBlank && !m) ||
30698 (!this.yearAllowBlank && !y)
30703 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30712 this.markInvalid();
30717 markValid : function()
30720 var label = this.el.select('label', true).first();
30721 var icon = this.el.select('i.fa-star', true).first();
30727 this.fireEvent('valid', this);
30731 * Mark this field as invalid
30732 * @param {String} msg The validation message
30734 markInvalid : function(msg)
30737 var label = this.el.select('label', true).first();
30738 var icon = this.el.select('i.fa-star', true).first();
30740 if(label && !icon){
30741 this.el.select('.roo-date-split-field-label', true).createChild({
30743 cls : 'text-danger fa fa-lg fa-star',
30744 tooltip : 'This field is required',
30745 style : 'margin-right:5px;'
30749 this.fireEvent('invalid', this, msg);
30752 clearInvalid : function()
30754 var label = this.el.select('label', true).first();
30755 var icon = this.el.select('i.fa-star', true).first();
30761 this.fireEvent('valid', this);
30764 getName: function()
30774 * http://masonry.desandro.com
30776 * The idea is to render all the bricks based on vertical width...
30778 * The original code extends 'outlayer' - we might need to use that....
30784 * @class Roo.bootstrap.LayoutMasonry
30785 * @extends Roo.bootstrap.Component
30786 * Bootstrap Layout Masonry class
30789 * Create a new Element
30790 * @param {Object} config The config object
30793 Roo.bootstrap.LayoutMasonry = function(config){
30795 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30799 Roo.bootstrap.LayoutMasonry.register(this);
30805 * Fire after layout the items
30806 * @param {Roo.bootstrap.LayoutMasonry} this
30807 * @param {Roo.EventObject} e
30814 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30817 * @cfg {Boolean} isLayoutInstant = no animation?
30819 isLayoutInstant : false, // needed?
30822 * @cfg {Number} boxWidth width of the columns
30827 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30832 * @cfg {Number} padWidth padding below box..
30837 * @cfg {Number} gutter gutter width..
30842 * @cfg {Number} maxCols maximum number of columns
30848 * @cfg {Boolean} isAutoInitial defalut true
30850 isAutoInitial : true,
30855 * @cfg {Boolean} isHorizontal defalut false
30857 isHorizontal : false,
30859 currentSize : null,
30865 bricks: null, //CompositeElement
30869 _isLayoutInited : false,
30871 // isAlternative : false, // only use for vertical layout...
30874 * @cfg {Number} alternativePadWidth padding below box..
30876 alternativePadWidth : 50,
30878 selectedBrick : [],
30880 getAutoCreate : function(){
30882 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30886 cls: 'blog-masonary-wrapper ' + this.cls,
30888 cls : 'mas-boxes masonary'
30895 getChildContainer: function( )
30897 if (this.boxesEl) {
30898 return this.boxesEl;
30901 this.boxesEl = this.el.select('.mas-boxes').first();
30903 return this.boxesEl;
30907 initEvents : function()
30911 if(this.isAutoInitial){
30912 Roo.log('hook children rendered');
30913 this.on('childrenrendered', function() {
30914 Roo.log('children rendered');
30920 initial : function()
30922 this.selectedBrick = [];
30924 this.currentSize = this.el.getBox(true);
30926 Roo.EventManager.onWindowResize(this.resize, this);
30928 if(!this.isAutoInitial){
30936 //this.layout.defer(500,this);
30940 resize : function()
30942 var cs = this.el.getBox(true);
30945 this.currentSize.width == cs.width &&
30946 this.currentSize.x == cs.x &&
30947 this.currentSize.height == cs.height &&
30948 this.currentSize.y == cs.y
30950 Roo.log("no change in with or X or Y");
30954 this.currentSize = cs;
30960 layout : function()
30962 this._resetLayout();
30964 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30966 this.layoutItems( isInstant );
30968 this._isLayoutInited = true;
30970 this.fireEvent('layout', this);
30974 _resetLayout : function()
30976 if(this.isHorizontal){
30977 this.horizontalMeasureColumns();
30981 this.verticalMeasureColumns();
30985 verticalMeasureColumns : function()
30987 this.getContainerWidth();
30989 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30990 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30994 var boxWidth = this.boxWidth + this.padWidth;
30996 if(this.containerWidth < this.boxWidth){
30997 boxWidth = this.containerWidth
31000 var containerWidth = this.containerWidth;
31002 var cols = Math.floor(containerWidth / boxWidth);
31004 this.cols = Math.max( cols, 1 );
31006 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31008 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31010 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31012 this.colWidth = boxWidth + avail - this.padWidth;
31014 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31015 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31018 horizontalMeasureColumns : function()
31020 this.getContainerWidth();
31022 var boxWidth = this.boxWidth;
31024 if(this.containerWidth < boxWidth){
31025 boxWidth = this.containerWidth;
31028 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31030 this.el.setHeight(boxWidth);
31034 getContainerWidth : function()
31036 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31039 layoutItems : function( isInstant )
31041 Roo.log(this.bricks);
31043 var items = Roo.apply([], this.bricks);
31045 if(this.isHorizontal){
31046 this._horizontalLayoutItems( items , isInstant );
31050 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31051 // this._verticalAlternativeLayoutItems( items , isInstant );
31055 this._verticalLayoutItems( items , isInstant );
31059 _verticalLayoutItems : function ( items , isInstant)
31061 if ( !items || !items.length ) {
31066 ['xs', 'xs', 'xs', 'tall'],
31067 ['xs', 'xs', 'tall'],
31068 ['xs', 'xs', 'sm'],
31069 ['xs', 'xs', 'xs'],
31075 ['sm', 'xs', 'xs'],
31079 ['tall', 'xs', 'xs', 'xs'],
31080 ['tall', 'xs', 'xs'],
31092 Roo.each(items, function(item, k){
31094 switch (item.size) {
31095 // these layouts take up a full box,
31106 boxes.push([item]);
31129 var filterPattern = function(box, length)
31137 var pattern = box.slice(0, length);
31141 Roo.each(pattern, function(i){
31142 format.push(i.size);
31145 Roo.each(standard, function(s){
31147 if(String(s) != String(format)){
31156 if(!match && length == 1){
31161 filterPattern(box, length - 1);
31165 queue.push(pattern);
31167 box = box.slice(length, box.length);
31169 filterPattern(box, 4);
31175 Roo.each(boxes, function(box, k){
31181 if(box.length == 1){
31186 filterPattern(box, 4);
31190 this._processVerticalLayoutQueue( queue, isInstant );
31194 // _verticalAlternativeLayoutItems : function( items , isInstant )
31196 // if ( !items || !items.length ) {
31200 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31204 _horizontalLayoutItems : function ( items , isInstant)
31206 if ( !items || !items.length || items.length < 3) {
31212 var eItems = items.slice(0, 3);
31214 items = items.slice(3, items.length);
31217 ['xs', 'xs', 'xs', 'wide'],
31218 ['xs', 'xs', 'wide'],
31219 ['xs', 'xs', 'sm'],
31220 ['xs', 'xs', 'xs'],
31226 ['sm', 'xs', 'xs'],
31230 ['wide', 'xs', 'xs', 'xs'],
31231 ['wide', 'xs', 'xs'],
31244 Roo.each(items, function(item, k){
31246 switch (item.size) {
31257 boxes.push([item]);
31281 var filterPattern = function(box, length)
31289 var pattern = box.slice(0, length);
31293 Roo.each(pattern, function(i){
31294 format.push(i.size);
31297 Roo.each(standard, function(s){
31299 if(String(s) != String(format)){
31308 if(!match && length == 1){
31313 filterPattern(box, length - 1);
31317 queue.push(pattern);
31319 box = box.slice(length, box.length);
31321 filterPattern(box, 4);
31327 Roo.each(boxes, function(box, k){
31333 if(box.length == 1){
31338 filterPattern(box, 4);
31345 var pos = this.el.getBox(true);
31349 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31351 var hit_end = false;
31353 Roo.each(queue, function(box){
31357 Roo.each(box, function(b){
31359 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31369 Roo.each(box, function(b){
31371 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31374 mx = Math.max(mx, b.x);
31378 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31382 Roo.each(box, function(b){
31384 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31398 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31401 /** Sets position of item in DOM
31402 * @param {Element} item
31403 * @param {Number} x - horizontal position
31404 * @param {Number} y - vertical position
31405 * @param {Boolean} isInstant - disables transitions
31407 _processVerticalLayoutQueue : function( queue, isInstant )
31409 var pos = this.el.getBox(true);
31414 for (var i = 0; i < this.cols; i++){
31418 Roo.each(queue, function(box, k){
31420 var col = k % this.cols;
31422 Roo.each(box, function(b,kk){
31424 b.el.position('absolute');
31426 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31427 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31429 if(b.size == 'md-left' || b.size == 'md-right'){
31430 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31431 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31434 b.el.setWidth(width);
31435 b.el.setHeight(height);
31437 b.el.select('iframe',true).setSize(width,height);
31441 for (var i = 0; i < this.cols; i++){
31443 if(maxY[i] < maxY[col]){
31448 col = Math.min(col, i);
31452 x = pos.x + col * (this.colWidth + this.padWidth);
31456 var positions = [];
31458 switch (box.length){
31460 positions = this.getVerticalOneBoxColPositions(x, y, box);
31463 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31466 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31469 positions = this.getVerticalFourBoxColPositions(x, y, box);
31475 Roo.each(box, function(b,kk){
31477 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31479 var sz = b.el.getSize();
31481 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31489 for (var i = 0; i < this.cols; i++){
31490 mY = Math.max(mY, maxY[i]);
31493 this.el.setHeight(mY - pos.y);
31497 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31499 // var pos = this.el.getBox(true);
31502 // var maxX = pos.right;
31504 // var maxHeight = 0;
31506 // Roo.each(items, function(item, k){
31510 // item.el.position('absolute');
31512 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31514 // item.el.setWidth(width);
31516 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31518 // item.el.setHeight(height);
31521 // item.el.setXY([x, y], isInstant ? false : true);
31523 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31526 // y = y + height + this.alternativePadWidth;
31528 // maxHeight = maxHeight + height + this.alternativePadWidth;
31532 // this.el.setHeight(maxHeight);
31536 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31538 var pos = this.el.getBox(true);
31543 var maxX = pos.right;
31545 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31547 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31549 Roo.each(queue, function(box, k){
31551 Roo.each(box, function(b, kk){
31553 b.el.position('absolute');
31555 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31556 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31558 if(b.size == 'md-left' || b.size == 'md-right'){
31559 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31560 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31563 b.el.setWidth(width);
31564 b.el.setHeight(height);
31572 var positions = [];
31574 switch (box.length){
31576 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31579 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31582 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31585 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31591 Roo.each(box, function(b,kk){
31593 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31595 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31603 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31605 Roo.each(eItems, function(b,k){
31607 b.size = (k == 0) ? 'sm' : 'xs';
31608 b.x = (k == 0) ? 2 : 1;
31609 b.y = (k == 0) ? 2 : 1;
31611 b.el.position('absolute');
31613 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31615 b.el.setWidth(width);
31617 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31619 b.el.setHeight(height);
31623 var positions = [];
31626 x : maxX - this.unitWidth * 2 - this.gutter,
31631 x : maxX - this.unitWidth,
31632 y : minY + (this.unitWidth + this.gutter) * 2
31636 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31640 Roo.each(eItems, function(b,k){
31642 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31648 getVerticalOneBoxColPositions : function(x, y, box)
31652 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31654 if(box[0].size == 'md-left'){
31658 if(box[0].size == 'md-right'){
31663 x : x + (this.unitWidth + this.gutter) * rand,
31670 getVerticalTwoBoxColPositions : function(x, y, box)
31674 if(box[0].size == 'xs'){
31678 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31682 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31696 x : x + (this.unitWidth + this.gutter) * 2,
31697 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31704 getVerticalThreeBoxColPositions : function(x, y, box)
31708 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31716 x : x + (this.unitWidth + this.gutter) * 1,
31721 x : x + (this.unitWidth + this.gutter) * 2,
31729 if(box[0].size == 'xs' && box[1].size == 'xs'){
31738 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31742 x : x + (this.unitWidth + this.gutter) * 1,
31756 x : x + (this.unitWidth + this.gutter) * 2,
31761 x : x + (this.unitWidth + this.gutter) * 2,
31762 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31769 getVerticalFourBoxColPositions : function(x, y, box)
31773 if(box[0].size == 'xs'){
31782 y : y + (this.unitHeight + this.gutter) * 1
31787 y : y + (this.unitHeight + this.gutter) * 2
31791 x : x + (this.unitWidth + this.gutter) * 1,
31805 x : x + (this.unitWidth + this.gutter) * 2,
31810 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31811 y : y + (this.unitHeight + this.gutter) * 1
31815 x : x + (this.unitWidth + this.gutter) * 2,
31816 y : y + (this.unitWidth + this.gutter) * 2
31823 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31827 if(box[0].size == 'md-left'){
31829 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31836 if(box[0].size == 'md-right'){
31838 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31839 y : minY + (this.unitWidth + this.gutter) * 1
31845 var rand = Math.floor(Math.random() * (4 - box[0].y));
31848 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31849 y : minY + (this.unitWidth + this.gutter) * rand
31856 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31860 if(box[0].size == 'xs'){
31863 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31868 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31869 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31877 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31882 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31883 y : minY + (this.unitWidth + this.gutter) * 2
31890 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31894 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31897 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31902 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31903 y : minY + (this.unitWidth + this.gutter) * 1
31907 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31908 y : minY + (this.unitWidth + this.gutter) * 2
31915 if(box[0].size == 'xs' && box[1].size == 'xs'){
31918 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31923 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31928 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31929 y : minY + (this.unitWidth + this.gutter) * 1
31937 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31942 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31943 y : minY + (this.unitWidth + this.gutter) * 2
31947 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31948 y : minY + (this.unitWidth + this.gutter) * 2
31955 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31959 if(box[0].size == 'xs'){
31962 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31967 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31972 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),
31977 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31978 y : minY + (this.unitWidth + this.gutter) * 1
31986 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31991 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31992 y : minY + (this.unitWidth + this.gutter) * 2
31996 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31997 y : minY + (this.unitWidth + this.gutter) * 2
32001 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),
32002 y : minY + (this.unitWidth + this.gutter) * 2
32010 * remove a Masonry Brick
32011 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32013 removeBrick : function(brick_id)
32019 for (var i = 0; i<this.bricks.length; i++) {
32020 if (this.bricks[i].id == brick_id) {
32021 this.bricks.splice(i,1);
32022 this.el.dom.removeChild(Roo.get(brick_id).dom);
32029 * adds a Masonry Brick
32030 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32032 addBrick : function(cfg)
32034 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32035 //this.register(cn);
32036 cn.parentId = this.id;
32037 cn.render(this.el);
32042 * register a Masonry Brick
32043 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32046 register : function(brick)
32048 this.bricks.push(brick);
32049 brick.masonryId = this.id;
32053 * clear all the Masonry Brick
32055 clearAll : function()
32058 //this.getChildContainer().dom.innerHTML = "";
32059 this.el.dom.innerHTML = '';
32062 getSelected : function()
32064 if (!this.selectedBrick) {
32068 return this.selectedBrick;
32072 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32076 * register a Masonry Layout
32077 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32080 register : function(layout)
32082 this.groups[layout.id] = layout;
32085 * fetch a Masonry Layout based on the masonry layout ID
32086 * @param {string} the masonry layout to add
32087 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32090 get: function(layout_id) {
32091 if (typeof(this.groups[layout_id]) == 'undefined') {
32094 return this.groups[layout_id] ;
32106 * http://masonry.desandro.com
32108 * The idea is to render all the bricks based on vertical width...
32110 * The original code extends 'outlayer' - we might need to use that....
32116 * @class Roo.bootstrap.LayoutMasonryAuto
32117 * @extends Roo.bootstrap.Component
32118 * Bootstrap Layout Masonry class
32121 * Create a new Element
32122 * @param {Object} config The config object
32125 Roo.bootstrap.LayoutMasonryAuto = function(config){
32126 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32129 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32132 * @cfg {Boolean} isFitWidth - resize the width..
32134 isFitWidth : false, // options..
32136 * @cfg {Boolean} isOriginLeft = left align?
32138 isOriginLeft : true,
32140 * @cfg {Boolean} isOriginTop = top align?
32142 isOriginTop : false,
32144 * @cfg {Boolean} isLayoutInstant = no animation?
32146 isLayoutInstant : false, // needed?
32148 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32150 isResizingContainer : true,
32152 * @cfg {Number} columnWidth width of the columns
32158 * @cfg {Number} maxCols maximum number of columns
32163 * @cfg {Number} padHeight padding below box..
32169 * @cfg {Boolean} isAutoInitial defalut true
32172 isAutoInitial : true,
32178 initialColumnWidth : 0,
32179 currentSize : null,
32181 colYs : null, // array.
32188 bricks: null, //CompositeElement
32189 cols : 0, // array?
32190 // element : null, // wrapped now this.el
32191 _isLayoutInited : null,
32194 getAutoCreate : function(){
32198 cls: 'blog-masonary-wrapper ' + this.cls,
32200 cls : 'mas-boxes masonary'
32207 getChildContainer: function( )
32209 if (this.boxesEl) {
32210 return this.boxesEl;
32213 this.boxesEl = this.el.select('.mas-boxes').first();
32215 return this.boxesEl;
32219 initEvents : function()
32223 if(this.isAutoInitial){
32224 Roo.log('hook children rendered');
32225 this.on('childrenrendered', function() {
32226 Roo.log('children rendered');
32233 initial : function()
32235 this.reloadItems();
32237 this.currentSize = this.el.getBox(true);
32239 /// was window resize... - let's see if this works..
32240 Roo.EventManager.onWindowResize(this.resize, this);
32242 if(!this.isAutoInitial){
32247 this.layout.defer(500,this);
32250 reloadItems: function()
32252 this.bricks = this.el.select('.masonry-brick', true);
32254 this.bricks.each(function(b) {
32255 //Roo.log(b.getSize());
32256 if (!b.attr('originalwidth')) {
32257 b.attr('originalwidth', b.getSize().width);
32262 Roo.log(this.bricks.elements.length);
32265 resize : function()
32268 var cs = this.el.getBox(true);
32270 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32271 Roo.log("no change in with or X");
32274 this.currentSize = cs;
32278 layout : function()
32281 this._resetLayout();
32282 //this._manageStamps();
32284 // don't animate first layout
32285 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32286 this.layoutItems( isInstant );
32288 // flag for initalized
32289 this._isLayoutInited = true;
32292 layoutItems : function( isInstant )
32294 //var items = this._getItemsForLayout( this.items );
32295 // original code supports filtering layout items.. we just ignore it..
32297 this._layoutItems( this.bricks , isInstant );
32299 this._postLayout();
32301 _layoutItems : function ( items , isInstant)
32303 //this.fireEvent( 'layout', this, items );
32306 if ( !items || !items.elements.length ) {
32307 // no items, emit event with empty array
32312 items.each(function(item) {
32313 Roo.log("layout item");
32315 // get x/y object from method
32316 var position = this._getItemLayoutPosition( item );
32318 position.item = item;
32319 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32320 queue.push( position );
32323 this._processLayoutQueue( queue );
32325 /** Sets position of item in DOM
32326 * @param {Element} item
32327 * @param {Number} x - horizontal position
32328 * @param {Number} y - vertical position
32329 * @param {Boolean} isInstant - disables transitions
32331 _processLayoutQueue : function( queue )
32333 for ( var i=0, len = queue.length; i < len; i++ ) {
32334 var obj = queue[i];
32335 obj.item.position('absolute');
32336 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32342 * Any logic you want to do after each layout,
32343 * i.e. size the container
32345 _postLayout : function()
32347 this.resizeContainer();
32350 resizeContainer : function()
32352 if ( !this.isResizingContainer ) {
32355 var size = this._getContainerSize();
32357 this.el.setSize(size.width,size.height);
32358 this.boxesEl.setSize(size.width,size.height);
32364 _resetLayout : function()
32366 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32367 this.colWidth = this.el.getWidth();
32368 //this.gutter = this.el.getWidth();
32370 this.measureColumns();
32376 this.colYs.push( 0 );
32382 measureColumns : function()
32384 this.getContainerWidth();
32385 // if columnWidth is 0, default to outerWidth of first item
32386 if ( !this.columnWidth ) {
32387 var firstItem = this.bricks.first();
32388 Roo.log(firstItem);
32389 this.columnWidth = this.containerWidth;
32390 if (firstItem && firstItem.attr('originalwidth') ) {
32391 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32393 // columnWidth fall back to item of first element
32394 Roo.log("set column width?");
32395 this.initialColumnWidth = this.columnWidth ;
32397 // if first elem has no width, default to size of container
32402 if (this.initialColumnWidth) {
32403 this.columnWidth = this.initialColumnWidth;
32408 // column width is fixed at the top - however if container width get's smaller we should
32411 // this bit calcs how man columns..
32413 var columnWidth = this.columnWidth += this.gutter;
32415 // calculate columns
32416 var containerWidth = this.containerWidth + this.gutter;
32418 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32419 // fix rounding errors, typically with gutters
32420 var excess = columnWidth - containerWidth % columnWidth;
32423 // if overshoot is less than a pixel, round up, otherwise floor it
32424 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32425 cols = Math[ mathMethod ]( cols );
32426 this.cols = Math.max( cols, 1 );
32427 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32429 // padding positioning..
32430 var totalColWidth = this.cols * this.columnWidth;
32431 var padavail = this.containerWidth - totalColWidth;
32432 // so for 2 columns - we need 3 'pads'
32434 var padNeeded = (1+this.cols) * this.padWidth;
32436 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32438 this.columnWidth += padExtra
32439 //this.padWidth = Math.floor(padavail / ( this.cols));
32441 // adjust colum width so that padding is fixed??
32443 // we have 3 columns ... total = width * 3
32444 // we have X left over... that should be used by
32446 //if (this.expandC) {
32454 getContainerWidth : function()
32456 /* // container is parent if fit width
32457 var container = this.isFitWidth ? this.element.parentNode : this.element;
32458 // check that this.size and size are there
32459 // IE8 triggers resize on body size change, so they might not be
32461 var size = getSize( container ); //FIXME
32462 this.containerWidth = size && size.innerWidth; //FIXME
32465 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32469 _getItemLayoutPosition : function( item ) // what is item?
32471 // we resize the item to our columnWidth..
32473 item.setWidth(this.columnWidth);
32474 item.autoBoxAdjust = false;
32476 var sz = item.getSize();
32478 // how many columns does this brick span
32479 var remainder = this.containerWidth % this.columnWidth;
32481 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32482 // round if off by 1 pixel, otherwise use ceil
32483 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32484 colSpan = Math.min( colSpan, this.cols );
32486 // normally this should be '1' as we dont' currently allow multi width columns..
32488 var colGroup = this._getColGroup( colSpan );
32489 // get the minimum Y value from the columns
32490 var minimumY = Math.min.apply( Math, colGroup );
32491 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32493 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32495 // position the brick
32497 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32498 y: this.currentSize.y + minimumY + this.padHeight
32502 // apply setHeight to necessary columns
32503 var setHeight = minimumY + sz.height + this.padHeight;
32504 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32506 var setSpan = this.cols + 1 - colGroup.length;
32507 for ( var i = 0; i < setSpan; i++ ) {
32508 this.colYs[ shortColIndex + i ] = setHeight ;
32515 * @param {Number} colSpan - number of columns the element spans
32516 * @returns {Array} colGroup
32518 _getColGroup : function( colSpan )
32520 if ( colSpan < 2 ) {
32521 // if brick spans only one column, use all the column Ys
32526 // how many different places could this brick fit horizontally
32527 var groupCount = this.cols + 1 - colSpan;
32528 // for each group potential horizontal position
32529 for ( var i = 0; i < groupCount; i++ ) {
32530 // make an array of colY values for that one group
32531 var groupColYs = this.colYs.slice( i, i + colSpan );
32532 // and get the max value of the array
32533 colGroup[i] = Math.max.apply( Math, groupColYs );
32538 _manageStamp : function( stamp )
32540 var stampSize = stamp.getSize();
32541 var offset = stamp.getBox();
32542 // get the columns that this stamp affects
32543 var firstX = this.isOriginLeft ? offset.x : offset.right;
32544 var lastX = firstX + stampSize.width;
32545 var firstCol = Math.floor( firstX / this.columnWidth );
32546 firstCol = Math.max( 0, firstCol );
32548 var lastCol = Math.floor( lastX / this.columnWidth );
32549 // lastCol should not go over if multiple of columnWidth #425
32550 lastCol -= lastX % this.columnWidth ? 0 : 1;
32551 lastCol = Math.min( this.cols - 1, lastCol );
32553 // set colYs to bottom of the stamp
32554 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32557 for ( var i = firstCol; i <= lastCol; i++ ) {
32558 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32563 _getContainerSize : function()
32565 this.maxY = Math.max.apply( Math, this.colYs );
32570 if ( this.isFitWidth ) {
32571 size.width = this._getContainerFitWidth();
32577 _getContainerFitWidth : function()
32579 var unusedCols = 0;
32580 // count unused columns
32583 if ( this.colYs[i] !== 0 ) {
32588 // fit container to columns that have been used
32589 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32592 needsResizeLayout : function()
32594 var previousWidth = this.containerWidth;
32595 this.getContainerWidth();
32596 return previousWidth !== this.containerWidth;
32611 * @class Roo.bootstrap.MasonryBrick
32612 * @extends Roo.bootstrap.Component
32613 * Bootstrap MasonryBrick class
32616 * Create a new MasonryBrick
32617 * @param {Object} config The config object
32620 Roo.bootstrap.MasonryBrick = function(config){
32622 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32624 Roo.bootstrap.MasonryBrick.register(this);
32630 * When a MasonryBrick is clcik
32631 * @param {Roo.bootstrap.MasonryBrick} this
32632 * @param {Roo.EventObject} e
32638 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32641 * @cfg {String} title
32645 * @cfg {String} html
32649 * @cfg {String} bgimage
32653 * @cfg {String} videourl
32657 * @cfg {String} cls
32661 * @cfg {String} href
32665 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32670 * @cfg {String} placetitle (center|bottom)
32675 * @cfg {Boolean} isFitContainer defalut true
32677 isFitContainer : true,
32680 * @cfg {Boolean} preventDefault defalut false
32682 preventDefault : false,
32685 * @cfg {Boolean} inverse defalut false
32687 maskInverse : false,
32689 getAutoCreate : function()
32691 if(!this.isFitContainer){
32692 return this.getSplitAutoCreate();
32695 var cls = 'masonry-brick masonry-brick-full';
32697 if(this.href.length){
32698 cls += ' masonry-brick-link';
32701 if(this.bgimage.length){
32702 cls += ' masonry-brick-image';
32705 if(this.maskInverse){
32706 cls += ' mask-inverse';
32709 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32710 cls += ' enable-mask';
32714 cls += ' masonry-' + this.size + '-brick';
32717 if(this.placetitle.length){
32719 switch (this.placetitle) {
32721 cls += ' masonry-center-title';
32724 cls += ' masonry-bottom-title';
32731 if(!this.html.length && !this.bgimage.length){
32732 cls += ' masonry-center-title';
32735 if(!this.html.length && this.bgimage.length){
32736 cls += ' masonry-bottom-title';
32741 cls += ' ' + this.cls;
32745 tag: (this.href.length) ? 'a' : 'div',
32750 cls: 'masonry-brick-mask'
32754 cls: 'masonry-brick-paragraph',
32760 if(this.href.length){
32761 cfg.href = this.href;
32764 var cn = cfg.cn[1].cn;
32766 if(this.title.length){
32769 cls: 'masonry-brick-title',
32774 if(this.html.length){
32777 cls: 'masonry-brick-text',
32782 if (!this.title.length && !this.html.length) {
32783 cfg.cn[1].cls += ' hide';
32786 if(this.bgimage.length){
32789 cls: 'masonry-brick-image-view',
32794 if(this.videourl.length){
32795 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32796 // youtube support only?
32799 cls: 'masonry-brick-image-view',
32802 allowfullscreen : true
32810 getSplitAutoCreate : function()
32812 var cls = 'masonry-brick masonry-brick-split';
32814 if(this.href.length){
32815 cls += ' masonry-brick-link';
32818 if(this.bgimage.length){
32819 cls += ' masonry-brick-image';
32823 cls += ' masonry-' + this.size + '-brick';
32826 switch (this.placetitle) {
32828 cls += ' masonry-center-title';
32831 cls += ' masonry-bottom-title';
32834 if(!this.bgimage.length){
32835 cls += ' masonry-center-title';
32838 if(this.bgimage.length){
32839 cls += ' masonry-bottom-title';
32845 cls += ' ' + this.cls;
32849 tag: (this.href.length) ? 'a' : 'div',
32854 cls: 'masonry-brick-split-head',
32858 cls: 'masonry-brick-paragraph',
32865 cls: 'masonry-brick-split-body',
32871 if(this.href.length){
32872 cfg.href = this.href;
32875 if(this.title.length){
32876 cfg.cn[0].cn[0].cn.push({
32878 cls: 'masonry-brick-title',
32883 if(this.html.length){
32884 cfg.cn[1].cn.push({
32886 cls: 'masonry-brick-text',
32891 if(this.bgimage.length){
32892 cfg.cn[0].cn.push({
32894 cls: 'masonry-brick-image-view',
32899 if(this.videourl.length){
32900 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32901 // youtube support only?
32902 cfg.cn[0].cn.cn.push({
32904 cls: 'masonry-brick-image-view',
32907 allowfullscreen : true
32914 initEvents: function()
32916 switch (this.size) {
32949 this.el.on('touchstart', this.onTouchStart, this);
32950 this.el.on('touchmove', this.onTouchMove, this);
32951 this.el.on('touchend', this.onTouchEnd, this);
32952 this.el.on('contextmenu', this.onContextMenu, this);
32954 this.el.on('mouseenter' ,this.enter, this);
32955 this.el.on('mouseleave', this.leave, this);
32956 this.el.on('click', this.onClick, this);
32959 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32960 this.parent().bricks.push(this);
32965 onClick: function(e, el)
32967 var time = this.endTimer - this.startTimer;
32968 // Roo.log(e.preventDefault());
32971 e.preventDefault();
32976 if(!this.preventDefault){
32980 e.preventDefault();
32982 if (this.activeClass != '') {
32983 this.selectBrick();
32986 this.fireEvent('click', this, e);
32989 enter: function(e, el)
32991 e.preventDefault();
32993 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32997 if(this.bgimage.length && this.html.length){
32998 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33002 leave: function(e, el)
33004 e.preventDefault();
33006 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33010 if(this.bgimage.length && this.html.length){
33011 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33015 onTouchStart: function(e, el)
33017 // e.preventDefault();
33019 this.touchmoved = false;
33021 if(!this.isFitContainer){
33025 if(!this.bgimage.length || !this.html.length){
33029 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33031 this.timer = new Date().getTime();
33035 onTouchMove: function(e, el)
33037 this.touchmoved = true;
33040 onContextMenu : function(e,el)
33042 e.preventDefault();
33043 e.stopPropagation();
33047 onTouchEnd: function(e, el)
33049 // e.preventDefault();
33051 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33058 if(!this.bgimage.length || !this.html.length){
33060 if(this.href.length){
33061 window.location.href = this.href;
33067 if(!this.isFitContainer){
33071 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33073 window.location.href = this.href;
33076 //selection on single brick only
33077 selectBrick : function() {
33079 if (!this.parentId) {
33083 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33084 var index = m.selectedBrick.indexOf(this.id);
33087 m.selectedBrick.splice(index,1);
33088 this.el.removeClass(this.activeClass);
33092 for(var i = 0; i < m.selectedBrick.length; i++) {
33093 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33094 b.el.removeClass(b.activeClass);
33097 m.selectedBrick = [];
33099 m.selectedBrick.push(this.id);
33100 this.el.addClass(this.activeClass);
33104 isSelected : function(){
33105 return this.el.hasClass(this.activeClass);
33110 Roo.apply(Roo.bootstrap.MasonryBrick, {
33113 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33115 * register a Masonry Brick
33116 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33119 register : function(brick)
33121 //this.groups[brick.id] = brick;
33122 this.groups.add(brick.id, brick);
33125 * fetch a masonry brick based on the masonry brick ID
33126 * @param {string} the masonry brick to add
33127 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33130 get: function(brick_id)
33132 // if (typeof(this.groups[brick_id]) == 'undefined') {
33135 // return this.groups[brick_id] ;
33137 if(this.groups.key(brick_id)) {
33138 return this.groups.key(brick_id);
33156 * @class Roo.bootstrap.Brick
33157 * @extends Roo.bootstrap.Component
33158 * Bootstrap Brick class
33161 * Create a new Brick
33162 * @param {Object} config The config object
33165 Roo.bootstrap.Brick = function(config){
33166 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33172 * When a Brick is click
33173 * @param {Roo.bootstrap.Brick} this
33174 * @param {Roo.EventObject} e
33180 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33183 * @cfg {String} title
33187 * @cfg {String} html
33191 * @cfg {String} bgimage
33195 * @cfg {String} cls
33199 * @cfg {String} href
33203 * @cfg {String} video
33207 * @cfg {Boolean} square
33211 getAutoCreate : function()
33213 var cls = 'roo-brick';
33215 if(this.href.length){
33216 cls += ' roo-brick-link';
33219 if(this.bgimage.length){
33220 cls += ' roo-brick-image';
33223 if(!this.html.length && !this.bgimage.length){
33224 cls += ' roo-brick-center-title';
33227 if(!this.html.length && this.bgimage.length){
33228 cls += ' roo-brick-bottom-title';
33232 cls += ' ' + this.cls;
33236 tag: (this.href.length) ? 'a' : 'div',
33241 cls: 'roo-brick-paragraph',
33247 if(this.href.length){
33248 cfg.href = this.href;
33251 var cn = cfg.cn[0].cn;
33253 if(this.title.length){
33256 cls: 'roo-brick-title',
33261 if(this.html.length){
33264 cls: 'roo-brick-text',
33271 if(this.bgimage.length){
33274 cls: 'roo-brick-image-view',
33282 initEvents: function()
33284 if(this.title.length || this.html.length){
33285 this.el.on('mouseenter' ,this.enter, this);
33286 this.el.on('mouseleave', this.leave, this);
33289 Roo.EventManager.onWindowResize(this.resize, this);
33291 if(this.bgimage.length){
33292 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33293 this.imageEl.on('load', this.onImageLoad, this);
33300 onImageLoad : function()
33305 resize : function()
33307 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33309 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33311 if(this.bgimage.length){
33312 var image = this.el.select('.roo-brick-image-view', true).first();
33314 image.setWidth(paragraph.getWidth());
33317 image.setHeight(paragraph.getWidth());
33320 this.el.setHeight(image.getHeight());
33321 paragraph.setHeight(image.getHeight());
33327 enter: function(e, el)
33329 e.preventDefault();
33331 if(this.bgimage.length){
33332 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33333 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33337 leave: function(e, el)
33339 e.preventDefault();
33341 if(this.bgimage.length){
33342 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33343 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33358 * @class Roo.bootstrap.NumberField
33359 * @extends Roo.bootstrap.Input
33360 * Bootstrap NumberField class
33366 * Create a new NumberField
33367 * @param {Object} config The config object
33370 Roo.bootstrap.NumberField = function(config){
33371 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33374 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33377 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33379 allowDecimals : true,
33381 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33383 decimalSeparator : ".",
33385 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33387 decimalPrecision : 2,
33389 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33391 allowNegative : true,
33394 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33398 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33400 minValue : Number.NEGATIVE_INFINITY,
33402 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33404 maxValue : Number.MAX_VALUE,
33406 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33408 minText : "The minimum value for this field is {0}",
33410 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33412 maxText : "The maximum value for this field is {0}",
33414 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33415 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33417 nanText : "{0} is not a valid number",
33419 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33421 thousandsDelimiter : false,
33423 * @cfg {String} valueAlign alignment of value
33425 valueAlign : "left",
33427 getAutoCreate : function()
33429 var hiddenInput = {
33433 cls: 'hidden-number-input'
33437 hiddenInput.name = this.name;
33442 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33444 this.name = hiddenInput.name;
33446 if(cfg.cn.length > 0) {
33447 cfg.cn.push(hiddenInput);
33454 initEvents : function()
33456 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33458 var allowed = "0123456789";
33460 if(this.allowDecimals){
33461 allowed += this.decimalSeparator;
33464 if(this.allowNegative){
33468 if(this.thousandsDelimiter) {
33472 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33474 var keyPress = function(e){
33476 var k = e.getKey();
33478 var c = e.getCharCode();
33481 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33482 allowed.indexOf(String.fromCharCode(c)) === -1
33488 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33492 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33497 this.el.on("keypress", keyPress, this);
33500 validateValue : function(value)
33503 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33507 var num = this.parseValue(value);
33510 this.markInvalid(String.format(this.nanText, value));
33514 if(num < this.minValue){
33515 this.markInvalid(String.format(this.minText, this.minValue));
33519 if(num > this.maxValue){
33520 this.markInvalid(String.format(this.maxText, this.maxValue));
33527 getValue : function()
33529 var v = this.hiddenEl().getValue();
33531 return this.fixPrecision(this.parseValue(v));
33534 parseValue : function(value)
33536 if(this.thousandsDelimiter) {
33538 r = new RegExp(",", "g");
33539 value = value.replace(r, "");
33542 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33543 return isNaN(value) ? '' : value;
33546 fixPrecision : function(value)
33548 if(this.thousandsDelimiter) {
33550 r = new RegExp(",", "g");
33551 value = value.replace(r, "");
33554 var nan = isNaN(value);
33556 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33557 return nan ? '' : value;
33559 return parseFloat(value).toFixed(this.decimalPrecision);
33562 setValue : function(v)
33564 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33570 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33572 this.inputEl().dom.value = (v == '') ? '' :
33573 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33575 if(!this.allowZero && v === '0') {
33576 this.hiddenEl().dom.value = '';
33577 this.inputEl().dom.value = '';
33584 decimalPrecisionFcn : function(v)
33586 return Math.floor(v);
33589 beforeBlur : function()
33591 var v = this.parseValue(this.getRawValue());
33593 if(v || v === 0 || v === ''){
33598 hiddenEl : function()
33600 return this.el.select('input.hidden-number-input',true).first();
33612 * @class Roo.bootstrap.DocumentSlider
33613 * @extends Roo.bootstrap.Component
33614 * Bootstrap DocumentSlider class
33617 * Create a new DocumentViewer
33618 * @param {Object} config The config object
33621 Roo.bootstrap.DocumentSlider = function(config){
33622 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33629 * Fire after initEvent
33630 * @param {Roo.bootstrap.DocumentSlider} this
33635 * Fire after update
33636 * @param {Roo.bootstrap.DocumentSlider} this
33642 * @param {Roo.bootstrap.DocumentSlider} this
33648 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33654 getAutoCreate : function()
33658 cls : 'roo-document-slider',
33662 cls : 'roo-document-slider-header',
33666 cls : 'roo-document-slider-header-title'
33672 cls : 'roo-document-slider-body',
33676 cls : 'roo-document-slider-prev',
33680 cls : 'fa fa-chevron-left'
33686 cls : 'roo-document-slider-thumb',
33690 cls : 'roo-document-slider-image'
33696 cls : 'roo-document-slider-next',
33700 cls : 'fa fa-chevron-right'
33712 initEvents : function()
33714 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33715 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33717 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33718 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33720 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33721 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33723 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33724 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33726 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33727 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33729 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33730 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33732 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33733 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33735 this.thumbEl.on('click', this.onClick, this);
33737 this.prevIndicator.on('click', this.prev, this);
33739 this.nextIndicator.on('click', this.next, this);
33743 initial : function()
33745 if(this.files.length){
33746 this.indicator = 1;
33750 this.fireEvent('initial', this);
33753 update : function()
33755 this.imageEl.attr('src', this.files[this.indicator - 1]);
33757 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33759 this.prevIndicator.show();
33761 if(this.indicator == 1){
33762 this.prevIndicator.hide();
33765 this.nextIndicator.show();
33767 if(this.indicator == this.files.length){
33768 this.nextIndicator.hide();
33771 this.thumbEl.scrollTo('top');
33773 this.fireEvent('update', this);
33776 onClick : function(e)
33778 e.preventDefault();
33780 this.fireEvent('click', this);
33785 e.preventDefault();
33787 this.indicator = Math.max(1, this.indicator - 1);
33794 e.preventDefault();
33796 this.indicator = Math.min(this.files.length, this.indicator + 1);
33810 * @class Roo.bootstrap.RadioSet
33811 * @extends Roo.bootstrap.Input
33812 * Bootstrap RadioSet class
33813 * @cfg {String} indicatorpos (left|right) default left
33814 * @cfg {Boolean} inline (true|false) inline the element (default true)
33815 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33817 * Create a new RadioSet
33818 * @param {Object} config The config object
33821 Roo.bootstrap.RadioSet = function(config){
33823 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33827 Roo.bootstrap.RadioSet.register(this);
33832 * Fires when the element is checked or unchecked.
33833 * @param {Roo.bootstrap.RadioSet} this This radio
33834 * @param {Roo.bootstrap.Radio} item The checked item
33839 * Fires when the element is click.
33840 * @param {Roo.bootstrap.RadioSet} this This radio set
33841 * @param {Roo.bootstrap.Radio} item The checked item
33842 * @param {Roo.EventObject} e The event object
33849 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33857 indicatorpos : 'left',
33859 getAutoCreate : function()
33863 cls : 'roo-radio-set-label',
33867 html : this.fieldLabel
33872 if(this.indicatorpos == 'left'){
33875 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33876 tooltip : 'This field is required'
33881 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33882 tooltip : 'This field is required'
33888 cls : 'roo-radio-set-items'
33891 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33893 if (align === 'left' && this.fieldLabel.length) {
33896 cls : "roo-radio-set-right",
33902 if(this.labelWidth > 12){
33903 label.style = "width: " + this.labelWidth + 'px';
33906 if(this.labelWidth < 13 && this.labelmd == 0){
33907 this.labelmd = this.labelWidth;
33910 if(this.labellg > 0){
33911 label.cls += ' col-lg-' + this.labellg;
33912 items.cls += ' col-lg-' + (12 - this.labellg);
33915 if(this.labelmd > 0){
33916 label.cls += ' col-md-' + this.labelmd;
33917 items.cls += ' col-md-' + (12 - this.labelmd);
33920 if(this.labelsm > 0){
33921 label.cls += ' col-sm-' + this.labelsm;
33922 items.cls += ' col-sm-' + (12 - this.labelsm);
33925 if(this.labelxs > 0){
33926 label.cls += ' col-xs-' + this.labelxs;
33927 items.cls += ' col-xs-' + (12 - this.labelxs);
33933 cls : 'roo-radio-set',
33937 cls : 'roo-radio-set-input',
33940 value : this.value ? this.value : ''
33947 if(this.weight.length){
33948 cfg.cls += ' roo-radio-' + this.weight;
33952 cfg.cls += ' roo-radio-set-inline';
33956 ['xs','sm','md','lg'].map(function(size){
33957 if (settings[size]) {
33958 cfg.cls += ' col-' + size + '-' + settings[size];
33966 initEvents : function()
33968 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33969 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33971 if(!this.fieldLabel.length){
33972 this.labelEl.hide();
33975 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33976 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33978 this.indicator = this.indicatorEl();
33980 if(this.indicator){
33981 this.indicator.addClass('invisible');
33984 this.originalValue = this.getValue();
33988 inputEl: function ()
33990 return this.el.select('.roo-radio-set-input', true).first();
33993 getChildContainer : function()
33995 return this.itemsEl;
33998 register : function(item)
34000 this.radioes.push(item);
34004 validate : function()
34006 if(this.getVisibilityEl().hasClass('hidden')){
34012 Roo.each(this.radioes, function(i){
34021 if(this.allowBlank) {
34025 if(this.disabled || valid){
34030 this.markInvalid();
34035 markValid : function()
34037 if(this.labelEl.isVisible(true)){
34038 this.indicatorEl().removeClass('visible');
34039 this.indicatorEl().addClass('invisible');
34042 this.el.removeClass([this.invalidClass, this.validClass]);
34043 this.el.addClass(this.validClass);
34045 this.fireEvent('valid', this);
34048 markInvalid : function(msg)
34050 if(this.allowBlank || this.disabled){
34054 if(this.labelEl.isVisible(true)){
34055 this.indicatorEl().removeClass('invisible');
34056 this.indicatorEl().addClass('visible');
34059 this.el.removeClass([this.invalidClass, this.validClass]);
34060 this.el.addClass(this.invalidClass);
34062 this.fireEvent('invalid', this, msg);
34066 setValue : function(v, suppressEvent)
34068 if(this.value === v){
34075 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34078 Roo.each(this.radioes, function(i){
34080 i.el.removeClass('checked');
34083 Roo.each(this.radioes, function(i){
34085 if(i.value === v || i.value.toString() === v.toString()){
34087 i.el.addClass('checked');
34089 if(suppressEvent !== true){
34090 this.fireEvent('check', this, i);
34101 clearInvalid : function(){
34103 if(!this.el || this.preventMark){
34107 this.el.removeClass([this.invalidClass]);
34109 this.fireEvent('valid', this);
34114 Roo.apply(Roo.bootstrap.RadioSet, {
34118 register : function(set)
34120 this.groups[set.name] = set;
34123 get: function(name)
34125 if (typeof(this.groups[name]) == 'undefined') {
34129 return this.groups[name] ;
34135 * Ext JS Library 1.1.1
34136 * Copyright(c) 2006-2007, Ext JS, LLC.
34138 * Originally Released Under LGPL - original licence link has changed is not relivant.
34141 * <script type="text/javascript">
34146 * @class Roo.bootstrap.SplitBar
34147 * @extends Roo.util.Observable
34148 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34152 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34153 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34154 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34155 split.minSize = 100;
34156 split.maxSize = 600;
34157 split.animate = true;
34158 split.on('moved', splitterMoved);
34161 * Create a new SplitBar
34162 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34163 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34164 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34165 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34166 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34167 position of the SplitBar).
34169 Roo.bootstrap.SplitBar = function(cfg){
34174 // dragElement : elm
34175 // resizingElement: el,
34177 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34178 // placement : Roo.bootstrap.SplitBar.LEFT ,
34179 // existingProxy ???
34182 this.el = Roo.get(cfg.dragElement, true);
34183 this.el.dom.unselectable = "on";
34185 this.resizingEl = Roo.get(cfg.resizingElement, true);
34189 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34190 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34193 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34196 * The minimum size of the resizing element. (Defaults to 0)
34202 * The maximum size of the resizing element. (Defaults to 2000)
34205 this.maxSize = 2000;
34208 * Whether to animate the transition to the new size
34211 this.animate = false;
34214 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34217 this.useShim = false;
34222 if(!cfg.existingProxy){
34224 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34226 this.proxy = Roo.get(cfg.existingProxy).dom;
34229 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34232 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34235 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34238 this.dragSpecs = {};
34241 * @private The adapter to use to positon and resize elements
34243 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34244 this.adapter.init(this);
34246 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34248 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34249 this.el.addClass("roo-splitbar-h");
34252 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34253 this.el.addClass("roo-splitbar-v");
34259 * Fires when the splitter is moved (alias for {@link #event-moved})
34260 * @param {Roo.bootstrap.SplitBar} this
34261 * @param {Number} newSize the new width or height
34266 * Fires when the splitter is moved
34267 * @param {Roo.bootstrap.SplitBar} this
34268 * @param {Number} newSize the new width or height
34272 * @event beforeresize
34273 * Fires before the splitter is dragged
34274 * @param {Roo.bootstrap.SplitBar} this
34276 "beforeresize" : true,
34278 "beforeapply" : true
34281 Roo.util.Observable.call(this);
34284 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34285 onStartProxyDrag : function(x, y){
34286 this.fireEvent("beforeresize", this);
34288 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34290 o.enableDisplayMode("block");
34291 // all splitbars share the same overlay
34292 Roo.bootstrap.SplitBar.prototype.overlay = o;
34294 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34295 this.overlay.show();
34296 Roo.get(this.proxy).setDisplayed("block");
34297 var size = this.adapter.getElementSize(this);
34298 this.activeMinSize = this.getMinimumSize();;
34299 this.activeMaxSize = this.getMaximumSize();;
34300 var c1 = size - this.activeMinSize;
34301 var c2 = Math.max(this.activeMaxSize - size, 0);
34302 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34303 this.dd.resetConstraints();
34304 this.dd.setXConstraint(
34305 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34306 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34308 this.dd.setYConstraint(0, 0);
34310 this.dd.resetConstraints();
34311 this.dd.setXConstraint(0, 0);
34312 this.dd.setYConstraint(
34313 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34314 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34317 this.dragSpecs.startSize = size;
34318 this.dragSpecs.startPoint = [x, y];
34319 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34323 * @private Called after the drag operation by the DDProxy
34325 onEndProxyDrag : function(e){
34326 Roo.get(this.proxy).setDisplayed(false);
34327 var endPoint = Roo.lib.Event.getXY(e);
34329 this.overlay.hide();
34332 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34333 newSize = this.dragSpecs.startSize +
34334 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34335 endPoint[0] - this.dragSpecs.startPoint[0] :
34336 this.dragSpecs.startPoint[0] - endPoint[0]
34339 newSize = this.dragSpecs.startSize +
34340 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34341 endPoint[1] - this.dragSpecs.startPoint[1] :
34342 this.dragSpecs.startPoint[1] - endPoint[1]
34345 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34346 if(newSize != this.dragSpecs.startSize){
34347 if(this.fireEvent('beforeapply', this, newSize) !== false){
34348 this.adapter.setElementSize(this, newSize);
34349 this.fireEvent("moved", this, newSize);
34350 this.fireEvent("resize", this, newSize);
34356 * Get the adapter this SplitBar uses
34357 * @return The adapter object
34359 getAdapter : function(){
34360 return this.adapter;
34364 * Set the adapter this SplitBar uses
34365 * @param {Object} adapter A SplitBar adapter object
34367 setAdapter : function(adapter){
34368 this.adapter = adapter;
34369 this.adapter.init(this);
34373 * Gets the minimum size for the resizing element
34374 * @return {Number} The minimum size
34376 getMinimumSize : function(){
34377 return this.minSize;
34381 * Sets the minimum size for the resizing element
34382 * @param {Number} minSize The minimum size
34384 setMinimumSize : function(minSize){
34385 this.minSize = minSize;
34389 * Gets the maximum size for the resizing element
34390 * @return {Number} The maximum size
34392 getMaximumSize : function(){
34393 return this.maxSize;
34397 * Sets the maximum size for the resizing element
34398 * @param {Number} maxSize The maximum size
34400 setMaximumSize : function(maxSize){
34401 this.maxSize = maxSize;
34405 * Sets the initialize size for the resizing element
34406 * @param {Number} size The initial size
34408 setCurrentSize : function(size){
34409 var oldAnimate = this.animate;
34410 this.animate = false;
34411 this.adapter.setElementSize(this, size);
34412 this.animate = oldAnimate;
34416 * Destroy this splitbar.
34417 * @param {Boolean} removeEl True to remove the element
34419 destroy : function(removeEl){
34421 this.shim.remove();
34424 this.proxy.parentNode.removeChild(this.proxy);
34432 * @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.
34434 Roo.bootstrap.SplitBar.createProxy = function(dir){
34435 var proxy = new Roo.Element(document.createElement("div"));
34436 proxy.unselectable();
34437 var cls = 'roo-splitbar-proxy';
34438 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34439 document.body.appendChild(proxy.dom);
34444 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34445 * Default Adapter. It assumes the splitter and resizing element are not positioned
34446 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34448 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34451 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34452 // do nothing for now
34453 init : function(s){
34457 * Called before drag operations to get the current size of the resizing element.
34458 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34460 getElementSize : function(s){
34461 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34462 return s.resizingEl.getWidth();
34464 return s.resizingEl.getHeight();
34469 * Called after drag operations to set the size of the resizing element.
34470 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34471 * @param {Number} newSize The new size to set
34472 * @param {Function} onComplete A function to be invoked when resizing is complete
34474 setElementSize : function(s, newSize, onComplete){
34475 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34477 s.resizingEl.setWidth(newSize);
34479 onComplete(s, newSize);
34482 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34487 s.resizingEl.setHeight(newSize);
34489 onComplete(s, newSize);
34492 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34499 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34500 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34501 * Adapter that moves the splitter element to align with the resized sizing element.
34502 * Used with an absolute positioned SplitBar.
34503 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34504 * document.body, make sure you assign an id to the body element.
34506 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34507 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34508 this.container = Roo.get(container);
34511 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34512 init : function(s){
34513 this.basic.init(s);
34516 getElementSize : function(s){
34517 return this.basic.getElementSize(s);
34520 setElementSize : function(s, newSize, onComplete){
34521 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34524 moveSplitter : function(s){
34525 var yes = Roo.bootstrap.SplitBar;
34526 switch(s.placement){
34528 s.el.setX(s.resizingEl.getRight());
34531 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34534 s.el.setY(s.resizingEl.getBottom());
34537 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34544 * Orientation constant - Create a vertical SplitBar
34548 Roo.bootstrap.SplitBar.VERTICAL = 1;
34551 * Orientation constant - Create a horizontal SplitBar
34555 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34558 * Placement constant - The resizing element is to the left of the splitter element
34562 Roo.bootstrap.SplitBar.LEFT = 1;
34565 * Placement constant - The resizing element is to the right of the splitter element
34569 Roo.bootstrap.SplitBar.RIGHT = 2;
34572 * Placement constant - The resizing element is positioned above the splitter element
34576 Roo.bootstrap.SplitBar.TOP = 3;
34579 * Placement constant - The resizing element is positioned under splitter element
34583 Roo.bootstrap.SplitBar.BOTTOM = 4;
34584 Roo.namespace("Roo.bootstrap.layout");/*
34586 * Ext JS Library 1.1.1
34587 * Copyright(c) 2006-2007, Ext JS, LLC.
34589 * Originally Released Under LGPL - original licence link has changed is not relivant.
34592 * <script type="text/javascript">
34596 * @class Roo.bootstrap.layout.Manager
34597 * @extends Roo.bootstrap.Component
34598 * Base class for layout managers.
34600 Roo.bootstrap.layout.Manager = function(config)
34602 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34608 /** false to disable window resize monitoring @type Boolean */
34609 this.monitorWindowResize = true;
34614 * Fires when a layout is performed.
34615 * @param {Roo.LayoutManager} this
34619 * @event regionresized
34620 * Fires when the user resizes a region.
34621 * @param {Roo.LayoutRegion} region The resized region
34622 * @param {Number} newSize The new size (width for east/west, height for north/south)
34624 "regionresized" : true,
34626 * @event regioncollapsed
34627 * Fires when a region is collapsed.
34628 * @param {Roo.LayoutRegion} region The collapsed region
34630 "regioncollapsed" : true,
34632 * @event regionexpanded
34633 * Fires when a region is expanded.
34634 * @param {Roo.LayoutRegion} region The expanded region
34636 "regionexpanded" : true
34638 this.updating = false;
34641 this.el = Roo.get(config.el);
34647 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34652 monitorWindowResize : true,
34658 onRender : function(ct, position)
34661 this.el = Roo.get(ct);
34664 //this.fireEvent('render',this);
34668 initEvents: function()
34672 // ie scrollbar fix
34673 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34674 document.body.scroll = "no";
34675 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34676 this.el.position('relative');
34678 this.id = this.el.id;
34679 this.el.addClass("roo-layout-container");
34680 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34681 if(this.el.dom != document.body ) {
34682 this.el.on('resize', this.layout,this);
34683 this.el.on('show', this.layout,this);
34689 * Returns true if this layout is currently being updated
34690 * @return {Boolean}
34692 isUpdating : function(){
34693 return this.updating;
34697 * Suspend the LayoutManager from doing auto-layouts while
34698 * making multiple add or remove calls
34700 beginUpdate : function(){
34701 this.updating = true;
34705 * Restore auto-layouts and optionally disable the manager from performing a layout
34706 * @param {Boolean} noLayout true to disable a layout update
34708 endUpdate : function(noLayout){
34709 this.updating = false;
34715 layout: function(){
34719 onRegionResized : function(region, newSize){
34720 this.fireEvent("regionresized", region, newSize);
34724 onRegionCollapsed : function(region){
34725 this.fireEvent("regioncollapsed", region);
34728 onRegionExpanded : function(region){
34729 this.fireEvent("regionexpanded", region);
34733 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34734 * performs box-model adjustments.
34735 * @return {Object} The size as an object {width: (the width), height: (the height)}
34737 getViewSize : function()
34740 if(this.el.dom != document.body){
34741 size = this.el.getSize();
34743 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34745 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34746 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34751 * Returns the Element this layout is bound to.
34752 * @return {Roo.Element}
34754 getEl : function(){
34759 * Returns the specified region.
34760 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34761 * @return {Roo.LayoutRegion}
34763 getRegion : function(target){
34764 return this.regions[target.toLowerCase()];
34767 onWindowResize : function(){
34768 if(this.monitorWindowResize){
34775 * Ext JS Library 1.1.1
34776 * Copyright(c) 2006-2007, Ext JS, LLC.
34778 * Originally Released Under LGPL - original licence link has changed is not relivant.
34781 * <script type="text/javascript">
34784 * @class Roo.bootstrap.layout.Border
34785 * @extends Roo.bootstrap.layout.Manager
34786 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34787 * please see: examples/bootstrap/nested.html<br><br>
34789 <b>The container the layout is rendered into can be either the body element or any other element.
34790 If it is not the body element, the container needs to either be an absolute positioned element,
34791 or you will need to add "position:relative" to the css of the container. You will also need to specify
34792 the container size if it is not the body element.</b>
34795 * Create a new Border
34796 * @param {Object} config Configuration options
34798 Roo.bootstrap.layout.Border = function(config){
34799 config = config || {};
34800 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34804 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34805 if(config[region]){
34806 config[region].region = region;
34807 this.addRegion(config[region]);
34813 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34815 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34817 * Creates and adds a new region if it doesn't already exist.
34818 * @param {String} target The target region key (north, south, east, west or center).
34819 * @param {Object} config The regions config object
34820 * @return {BorderLayoutRegion} The new region
34822 addRegion : function(config)
34824 if(!this.regions[config.region]){
34825 var r = this.factory(config);
34826 this.bindRegion(r);
34828 return this.regions[config.region];
34832 bindRegion : function(r){
34833 this.regions[r.config.region] = r;
34835 r.on("visibilitychange", this.layout, this);
34836 r.on("paneladded", this.layout, this);
34837 r.on("panelremoved", this.layout, this);
34838 r.on("invalidated", this.layout, this);
34839 r.on("resized", this.onRegionResized, this);
34840 r.on("collapsed", this.onRegionCollapsed, this);
34841 r.on("expanded", this.onRegionExpanded, this);
34845 * Performs a layout update.
34847 layout : function()
34849 if(this.updating) {
34853 // render all the rebions if they have not been done alreayd?
34854 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34855 if(this.regions[region] && !this.regions[region].bodyEl){
34856 this.regions[region].onRender(this.el)
34860 var size = this.getViewSize();
34861 var w = size.width;
34862 var h = size.height;
34867 //var x = 0, y = 0;
34869 var rs = this.regions;
34870 var north = rs["north"];
34871 var south = rs["south"];
34872 var west = rs["west"];
34873 var east = rs["east"];
34874 var center = rs["center"];
34875 //if(this.hideOnLayout){ // not supported anymore
34876 //c.el.setStyle("display", "none");
34878 if(north && north.isVisible()){
34879 var b = north.getBox();
34880 var m = north.getMargins();
34881 b.width = w - (m.left+m.right);
34884 centerY = b.height + b.y + m.bottom;
34885 centerH -= centerY;
34886 north.updateBox(this.safeBox(b));
34888 if(south && south.isVisible()){
34889 var b = south.getBox();
34890 var m = south.getMargins();
34891 b.width = w - (m.left+m.right);
34893 var totalHeight = (b.height + m.top + m.bottom);
34894 b.y = h - totalHeight + m.top;
34895 centerH -= totalHeight;
34896 south.updateBox(this.safeBox(b));
34898 if(west && west.isVisible()){
34899 var b = west.getBox();
34900 var m = west.getMargins();
34901 b.height = centerH - (m.top+m.bottom);
34903 b.y = centerY + m.top;
34904 var totalWidth = (b.width + m.left + m.right);
34905 centerX += totalWidth;
34906 centerW -= totalWidth;
34907 west.updateBox(this.safeBox(b));
34909 if(east && east.isVisible()){
34910 var b = east.getBox();
34911 var m = east.getMargins();
34912 b.height = centerH - (m.top+m.bottom);
34913 var totalWidth = (b.width + m.left + m.right);
34914 b.x = w - totalWidth + m.left;
34915 b.y = centerY + m.top;
34916 centerW -= totalWidth;
34917 east.updateBox(this.safeBox(b));
34920 var m = center.getMargins();
34922 x: centerX + m.left,
34923 y: centerY + m.top,
34924 width: centerW - (m.left+m.right),
34925 height: centerH - (m.top+m.bottom)
34927 //if(this.hideOnLayout){
34928 //center.el.setStyle("display", "block");
34930 center.updateBox(this.safeBox(centerBox));
34933 this.fireEvent("layout", this);
34937 safeBox : function(box){
34938 box.width = Math.max(0, box.width);
34939 box.height = Math.max(0, box.height);
34944 * Adds a ContentPanel (or subclass) to this layout.
34945 * @param {String} target The target region key (north, south, east, west or center).
34946 * @param {Roo.ContentPanel} panel The panel to add
34947 * @return {Roo.ContentPanel} The added panel
34949 add : function(target, panel){
34951 target = target.toLowerCase();
34952 return this.regions[target].add(panel);
34956 * Remove a ContentPanel (or subclass) to this layout.
34957 * @param {String} target The target region key (north, south, east, west or center).
34958 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34959 * @return {Roo.ContentPanel} The removed panel
34961 remove : function(target, panel){
34962 target = target.toLowerCase();
34963 return this.regions[target].remove(panel);
34967 * Searches all regions for a panel with the specified id
34968 * @param {String} panelId
34969 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34971 findPanel : function(panelId){
34972 var rs = this.regions;
34973 for(var target in rs){
34974 if(typeof rs[target] != "function"){
34975 var p = rs[target].getPanel(panelId);
34985 * Searches all regions for a panel with the specified id and activates (shows) it.
34986 * @param {String/ContentPanel} panelId The panels id or the panel itself
34987 * @return {Roo.ContentPanel} The shown panel or null
34989 showPanel : function(panelId) {
34990 var rs = this.regions;
34991 for(var target in rs){
34992 var r = rs[target];
34993 if(typeof r != "function"){
34994 if(r.hasPanel(panelId)){
34995 return r.showPanel(panelId);
35003 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35004 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35007 restoreState : function(provider){
35009 provider = Roo.state.Manager;
35011 var sm = new Roo.LayoutStateManager();
35012 sm.init(this, provider);
35018 * Adds a xtype elements to the layout.
35022 xtype : 'ContentPanel',
35029 xtype : 'NestedLayoutPanel',
35035 items : [ ... list of content panels or nested layout panels.. ]
35039 * @param {Object} cfg Xtype definition of item to add.
35041 addxtype : function(cfg)
35043 // basically accepts a pannel...
35044 // can accept a layout region..!?!?
35045 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35048 // theory? children can only be panels??
35050 //if (!cfg.xtype.match(/Panel$/)) {
35055 if (typeof(cfg.region) == 'undefined') {
35056 Roo.log("Failed to add Panel, region was not set");
35060 var region = cfg.region;
35066 xitems = cfg.items;
35073 case 'Content': // ContentPanel (el, cfg)
35074 case 'Scroll': // ContentPanel (el, cfg)
35076 cfg.autoCreate = true;
35077 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35079 // var el = this.el.createChild();
35080 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35083 this.add(region, ret);
35087 case 'TreePanel': // our new panel!
35088 cfg.el = this.el.createChild();
35089 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35090 this.add(region, ret);
35095 // create a new Layout (which is a Border Layout...
35097 var clayout = cfg.layout;
35098 clayout.el = this.el.createChild();
35099 clayout.items = clayout.items || [];
35103 // replace this exitems with the clayout ones..
35104 xitems = clayout.items;
35106 // force background off if it's in center...
35107 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35108 cfg.background = false;
35110 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35113 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35114 //console.log('adding nested layout panel ' + cfg.toSource());
35115 this.add(region, ret);
35116 nb = {}; /// find first...
35121 // needs grid and region
35123 //var el = this.getRegion(region).el.createChild();
35125 *var el = this.el.createChild();
35126 // create the grid first...
35127 cfg.grid.container = el;
35128 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35131 if (region == 'center' && this.active ) {
35132 cfg.background = false;
35135 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35137 this.add(region, ret);
35139 if (cfg.background) {
35140 // render grid on panel activation (if panel background)
35141 ret.on('activate', function(gp) {
35142 if (!gp.grid.rendered) {
35143 // gp.grid.render(el);
35147 // cfg.grid.render(el);
35153 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35154 // it was the old xcomponent building that caused this before.
35155 // espeically if border is the top element in the tree.
35165 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35167 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35168 this.add(region, ret);
35172 throw "Can not add '" + cfg.xtype + "' to Border";
35178 this.beginUpdate();
35182 Roo.each(xitems, function(i) {
35183 region = nb && i.region ? i.region : false;
35185 var add = ret.addxtype(i);
35188 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35189 if (!i.background) {
35190 abn[region] = nb[region] ;
35197 // make the last non-background panel active..
35198 //if (nb) { Roo.log(abn); }
35201 for(var r in abn) {
35202 region = this.getRegion(r);
35204 // tried using nb[r], but it does not work..
35206 region.showPanel(abn[r]);
35217 factory : function(cfg)
35220 var validRegions = Roo.bootstrap.layout.Border.regions;
35222 var target = cfg.region;
35225 var r = Roo.bootstrap.layout;
35229 return new r.North(cfg);
35231 return new r.South(cfg);
35233 return new r.East(cfg);
35235 return new r.West(cfg);
35237 return new r.Center(cfg);
35239 throw 'Layout region "'+target+'" not supported.';
35246 * Ext JS Library 1.1.1
35247 * Copyright(c) 2006-2007, Ext JS, LLC.
35249 * Originally Released Under LGPL - original licence link has changed is not relivant.
35252 * <script type="text/javascript">
35256 * @class Roo.bootstrap.layout.Basic
35257 * @extends Roo.util.Observable
35258 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35259 * and does not have a titlebar, tabs or any other features. All it does is size and position
35260 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35261 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35262 * @cfg {string} region the region that it inhabits..
35263 * @cfg {bool} skipConfig skip config?
35267 Roo.bootstrap.layout.Basic = function(config){
35269 this.mgr = config.mgr;
35271 this.position = config.region;
35273 var skipConfig = config.skipConfig;
35277 * @scope Roo.BasicLayoutRegion
35281 * @event beforeremove
35282 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35283 * @param {Roo.LayoutRegion} this
35284 * @param {Roo.ContentPanel} panel The panel
35285 * @param {Object} e The cancel event object
35287 "beforeremove" : true,
35289 * @event invalidated
35290 * Fires when the layout for this region is changed.
35291 * @param {Roo.LayoutRegion} this
35293 "invalidated" : true,
35295 * @event visibilitychange
35296 * Fires when this region is shown or hidden
35297 * @param {Roo.LayoutRegion} this
35298 * @param {Boolean} visibility true or false
35300 "visibilitychange" : true,
35302 * @event paneladded
35303 * Fires when a panel is added.
35304 * @param {Roo.LayoutRegion} this
35305 * @param {Roo.ContentPanel} panel The panel
35307 "paneladded" : true,
35309 * @event panelremoved
35310 * Fires when a panel is removed.
35311 * @param {Roo.LayoutRegion} this
35312 * @param {Roo.ContentPanel} panel The panel
35314 "panelremoved" : true,
35316 * @event beforecollapse
35317 * Fires when this region before collapse.
35318 * @param {Roo.LayoutRegion} this
35320 "beforecollapse" : true,
35323 * Fires when this region is collapsed.
35324 * @param {Roo.LayoutRegion} this
35326 "collapsed" : true,
35329 * Fires when this region is expanded.
35330 * @param {Roo.LayoutRegion} this
35335 * Fires when this region is slid into view.
35336 * @param {Roo.LayoutRegion} this
35338 "slideshow" : true,
35341 * Fires when this region slides out of view.
35342 * @param {Roo.LayoutRegion} this
35344 "slidehide" : true,
35346 * @event panelactivated
35347 * Fires when a panel is activated.
35348 * @param {Roo.LayoutRegion} this
35349 * @param {Roo.ContentPanel} panel The activated panel
35351 "panelactivated" : true,
35354 * Fires when the user resizes this region.
35355 * @param {Roo.LayoutRegion} this
35356 * @param {Number} newSize The new size (width for east/west, height for north/south)
35360 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35361 this.panels = new Roo.util.MixedCollection();
35362 this.panels.getKey = this.getPanelId.createDelegate(this);
35364 this.activePanel = null;
35365 // ensure listeners are added...
35367 if (config.listeners || config.events) {
35368 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35369 listeners : config.listeners || {},
35370 events : config.events || {}
35374 if(skipConfig !== true){
35375 this.applyConfig(config);
35379 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35381 getPanelId : function(p){
35385 applyConfig : function(config){
35386 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35387 this.config = config;
35392 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35393 * the width, for horizontal (north, south) the height.
35394 * @param {Number} newSize The new width or height
35396 resizeTo : function(newSize){
35397 var el = this.el ? this.el :
35398 (this.activePanel ? this.activePanel.getEl() : null);
35400 switch(this.position){
35403 el.setWidth(newSize);
35404 this.fireEvent("resized", this, newSize);
35408 el.setHeight(newSize);
35409 this.fireEvent("resized", this, newSize);
35415 getBox : function(){
35416 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35419 getMargins : function(){
35420 return this.margins;
35423 updateBox : function(box){
35425 var el = this.activePanel.getEl();
35426 el.dom.style.left = box.x + "px";
35427 el.dom.style.top = box.y + "px";
35428 this.activePanel.setSize(box.width, box.height);
35432 * Returns the container element for this region.
35433 * @return {Roo.Element}
35435 getEl : function(){
35436 return this.activePanel;
35440 * Returns true if this region is currently visible.
35441 * @return {Boolean}
35443 isVisible : function(){
35444 return this.activePanel ? true : false;
35447 setActivePanel : function(panel){
35448 panel = this.getPanel(panel);
35449 if(this.activePanel && this.activePanel != panel){
35450 this.activePanel.setActiveState(false);
35451 this.activePanel.getEl().setLeftTop(-10000,-10000);
35453 this.activePanel = panel;
35454 panel.setActiveState(true);
35456 panel.setSize(this.box.width, this.box.height);
35458 this.fireEvent("panelactivated", this, panel);
35459 this.fireEvent("invalidated");
35463 * Show the specified panel.
35464 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35465 * @return {Roo.ContentPanel} The shown panel or null
35467 showPanel : function(panel){
35468 panel = this.getPanel(panel);
35470 this.setActivePanel(panel);
35476 * Get the active panel for this region.
35477 * @return {Roo.ContentPanel} The active panel or null
35479 getActivePanel : function(){
35480 return this.activePanel;
35484 * Add the passed ContentPanel(s)
35485 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35486 * @return {Roo.ContentPanel} The panel added (if only one was added)
35488 add : function(panel){
35489 if(arguments.length > 1){
35490 for(var i = 0, len = arguments.length; i < len; i++) {
35491 this.add(arguments[i]);
35495 if(this.hasPanel(panel)){
35496 this.showPanel(panel);
35499 var el = panel.getEl();
35500 if(el.dom.parentNode != this.mgr.el.dom){
35501 this.mgr.el.dom.appendChild(el.dom);
35503 if(panel.setRegion){
35504 panel.setRegion(this);
35506 this.panels.add(panel);
35507 el.setStyle("position", "absolute");
35508 if(!panel.background){
35509 this.setActivePanel(panel);
35510 if(this.config.initialSize && this.panels.getCount()==1){
35511 this.resizeTo(this.config.initialSize);
35514 this.fireEvent("paneladded", this, panel);
35519 * Returns true if the panel is in this region.
35520 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35521 * @return {Boolean}
35523 hasPanel : function(panel){
35524 if(typeof panel == "object"){ // must be panel obj
35525 panel = panel.getId();
35527 return this.getPanel(panel) ? true : false;
35531 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35532 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35533 * @param {Boolean} preservePanel Overrides the config preservePanel option
35534 * @return {Roo.ContentPanel} The panel that was removed
35536 remove : function(panel, preservePanel){
35537 panel = this.getPanel(panel);
35542 this.fireEvent("beforeremove", this, panel, e);
35543 if(e.cancel === true){
35546 var panelId = panel.getId();
35547 this.panels.removeKey(panelId);
35552 * Returns the panel specified or null if it's not in this region.
35553 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35554 * @return {Roo.ContentPanel}
35556 getPanel : function(id){
35557 if(typeof id == "object"){ // must be panel obj
35560 return this.panels.get(id);
35564 * Returns this regions position (north/south/east/west/center).
35567 getPosition: function(){
35568 return this.position;
35572 * Ext JS Library 1.1.1
35573 * Copyright(c) 2006-2007, Ext JS, LLC.
35575 * Originally Released Under LGPL - original licence link has changed is not relivant.
35578 * <script type="text/javascript">
35582 * @class Roo.bootstrap.layout.Region
35583 * @extends Roo.bootstrap.layout.Basic
35584 * This class represents a region in a layout manager.
35586 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35587 * @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})
35588 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35589 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35590 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35591 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35592 * @cfg {String} title The title for the region (overrides panel titles)
35593 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35594 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35595 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35596 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35597 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35598 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35599 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35600 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35601 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35602 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35604 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35605 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35606 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35607 * @cfg {Number} width For East/West panels
35608 * @cfg {Number} height For North/South panels
35609 * @cfg {Boolean} split To show the splitter
35610 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35612 * @cfg {string} cls Extra CSS classes to add to region
35614 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35615 * @cfg {string} region the region that it inhabits..
35618 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35619 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35621 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35622 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35623 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35625 Roo.bootstrap.layout.Region = function(config)
35627 this.applyConfig(config);
35629 var mgr = config.mgr;
35630 var pos = config.region;
35631 config.skipConfig = true;
35632 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35635 this.onRender(mgr.el);
35638 this.visible = true;
35639 this.collapsed = false;
35640 this.unrendered_panels = [];
35643 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35645 position: '', // set by wrapper (eg. north/south etc..)
35646 unrendered_panels : null, // unrendered panels.
35647 createBody : function(){
35648 /** This region's body element
35649 * @type Roo.Element */
35650 this.bodyEl = this.el.createChild({
35652 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35656 onRender: function(ctr, pos)
35658 var dh = Roo.DomHelper;
35659 /** This region's container element
35660 * @type Roo.Element */
35661 this.el = dh.append(ctr.dom, {
35663 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35665 /** This region's title element
35666 * @type Roo.Element */
35668 this.titleEl = dh.append(this.el.dom,
35671 unselectable: "on",
35672 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35674 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35675 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35678 this.titleEl.enableDisplayMode();
35679 /** This region's title text element
35680 * @type HTMLElement */
35681 this.titleTextEl = this.titleEl.dom.firstChild;
35682 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35684 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35685 this.closeBtn.enableDisplayMode();
35686 this.closeBtn.on("click", this.closeClicked, this);
35687 this.closeBtn.hide();
35689 this.createBody(this.config);
35690 if(this.config.hideWhenEmpty){
35692 this.on("paneladded", this.validateVisibility, this);
35693 this.on("panelremoved", this.validateVisibility, this);
35695 if(this.autoScroll){
35696 this.bodyEl.setStyle("overflow", "auto");
35698 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35700 //if(c.titlebar !== false){
35701 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35702 this.titleEl.hide();
35704 this.titleEl.show();
35705 if(this.config.title){
35706 this.titleTextEl.innerHTML = this.config.title;
35710 if(this.config.collapsed){
35711 this.collapse(true);
35713 if(this.config.hidden){
35717 if (this.unrendered_panels && this.unrendered_panels.length) {
35718 for (var i =0;i< this.unrendered_panels.length; i++) {
35719 this.add(this.unrendered_panels[i]);
35721 this.unrendered_panels = null;
35727 applyConfig : function(c)
35730 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35731 var dh = Roo.DomHelper;
35732 if(c.titlebar !== false){
35733 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35734 this.collapseBtn.on("click", this.collapse, this);
35735 this.collapseBtn.enableDisplayMode();
35737 if(c.showPin === true || this.showPin){
35738 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35739 this.stickBtn.enableDisplayMode();
35740 this.stickBtn.on("click", this.expand, this);
35741 this.stickBtn.hide();
35746 /** This region's collapsed element
35747 * @type Roo.Element */
35750 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35751 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35754 if(c.floatable !== false){
35755 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35756 this.collapsedEl.on("click", this.collapseClick, this);
35759 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35760 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35761 id: "message", unselectable: "on", style:{"float":"left"}});
35762 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35764 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35765 this.expandBtn.on("click", this.expand, this);
35769 if(this.collapseBtn){
35770 this.collapseBtn.setVisible(c.collapsible == true);
35773 this.cmargins = c.cmargins || this.cmargins ||
35774 (this.position == "west" || this.position == "east" ?
35775 {top: 0, left: 2, right:2, bottom: 0} :
35776 {top: 2, left: 0, right:0, bottom: 2});
35778 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35781 this.bottomTabs = c.tabPosition != "top";
35783 this.autoScroll = c.autoScroll || false;
35788 this.duration = c.duration || .30;
35789 this.slideDuration = c.slideDuration || .45;
35794 * Returns true if this region is currently visible.
35795 * @return {Boolean}
35797 isVisible : function(){
35798 return this.visible;
35802 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35803 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35805 //setCollapsedTitle : function(title){
35806 // title = title || " ";
35807 // if(this.collapsedTitleTextEl){
35808 // this.collapsedTitleTextEl.innerHTML = title;
35812 getBox : function(){
35814 // if(!this.collapsed){
35815 b = this.el.getBox(false, true);
35817 // b = this.collapsedEl.getBox(false, true);
35822 getMargins : function(){
35823 return this.margins;
35824 //return this.collapsed ? this.cmargins : this.margins;
35827 highlight : function(){
35828 this.el.addClass("x-layout-panel-dragover");
35831 unhighlight : function(){
35832 this.el.removeClass("x-layout-panel-dragover");
35835 updateBox : function(box)
35837 if (!this.bodyEl) {
35838 return; // not rendered yet..
35842 if(!this.collapsed){
35843 this.el.dom.style.left = box.x + "px";
35844 this.el.dom.style.top = box.y + "px";
35845 this.updateBody(box.width, box.height);
35847 this.collapsedEl.dom.style.left = box.x + "px";
35848 this.collapsedEl.dom.style.top = box.y + "px";
35849 this.collapsedEl.setSize(box.width, box.height);
35852 this.tabs.autoSizeTabs();
35856 updateBody : function(w, h)
35859 this.el.setWidth(w);
35860 w -= this.el.getBorderWidth("rl");
35861 if(this.config.adjustments){
35862 w += this.config.adjustments[0];
35865 if(h !== null && h > 0){
35866 this.el.setHeight(h);
35867 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35868 h -= this.el.getBorderWidth("tb");
35869 if(this.config.adjustments){
35870 h += this.config.adjustments[1];
35872 this.bodyEl.setHeight(h);
35874 h = this.tabs.syncHeight(h);
35877 if(this.panelSize){
35878 w = w !== null ? w : this.panelSize.width;
35879 h = h !== null ? h : this.panelSize.height;
35881 if(this.activePanel){
35882 var el = this.activePanel.getEl();
35883 w = w !== null ? w : el.getWidth();
35884 h = h !== null ? h : el.getHeight();
35885 this.panelSize = {width: w, height: h};
35886 this.activePanel.setSize(w, h);
35888 if(Roo.isIE && this.tabs){
35889 this.tabs.el.repaint();
35894 * Returns the container element for this region.
35895 * @return {Roo.Element}
35897 getEl : function(){
35902 * Hides this region.
35905 //if(!this.collapsed){
35906 this.el.dom.style.left = "-2000px";
35909 // this.collapsedEl.dom.style.left = "-2000px";
35910 // this.collapsedEl.hide();
35912 this.visible = false;
35913 this.fireEvent("visibilitychange", this, false);
35917 * Shows this region if it was previously hidden.
35920 //if(!this.collapsed){
35923 // this.collapsedEl.show();
35925 this.visible = true;
35926 this.fireEvent("visibilitychange", this, true);
35929 closeClicked : function(){
35930 if(this.activePanel){
35931 this.remove(this.activePanel);
35935 collapseClick : function(e){
35937 e.stopPropagation();
35940 e.stopPropagation();
35946 * Collapses this region.
35947 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35950 collapse : function(skipAnim, skipCheck = false){
35951 if(this.collapsed) {
35955 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35957 this.collapsed = true;
35959 this.split.el.hide();
35961 if(this.config.animate && skipAnim !== true){
35962 this.fireEvent("invalidated", this);
35963 this.animateCollapse();
35965 this.el.setLocation(-20000,-20000);
35967 this.collapsedEl.show();
35968 this.fireEvent("collapsed", this);
35969 this.fireEvent("invalidated", this);
35975 animateCollapse : function(){
35980 * Expands this region if it was previously collapsed.
35981 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35982 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35985 expand : function(e, skipAnim){
35987 e.stopPropagation();
35989 if(!this.collapsed || this.el.hasActiveFx()) {
35993 this.afterSlideIn();
35996 this.collapsed = false;
35997 if(this.config.animate && skipAnim !== true){
35998 this.animateExpand();
36002 this.split.el.show();
36004 this.collapsedEl.setLocation(-2000,-2000);
36005 this.collapsedEl.hide();
36006 this.fireEvent("invalidated", this);
36007 this.fireEvent("expanded", this);
36011 animateExpand : function(){
36015 initTabs : function()
36017 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36019 var ts = new Roo.bootstrap.panel.Tabs({
36020 el: this.bodyEl.dom,
36021 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36022 disableTooltips: this.config.disableTabTips,
36023 toolbar : this.config.toolbar
36026 if(this.config.hideTabs){
36027 ts.stripWrap.setDisplayed(false);
36030 ts.resizeTabs = this.config.resizeTabs === true;
36031 ts.minTabWidth = this.config.minTabWidth || 40;
36032 ts.maxTabWidth = this.config.maxTabWidth || 250;
36033 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36034 ts.monitorResize = false;
36035 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36036 ts.bodyEl.addClass('roo-layout-tabs-body');
36037 this.panels.each(this.initPanelAsTab, this);
36040 initPanelAsTab : function(panel){
36041 var ti = this.tabs.addTab(
36045 this.config.closeOnTab && panel.isClosable(),
36048 if(panel.tabTip !== undefined){
36049 ti.setTooltip(panel.tabTip);
36051 ti.on("activate", function(){
36052 this.setActivePanel(panel);
36055 if(this.config.closeOnTab){
36056 ti.on("beforeclose", function(t, e){
36058 this.remove(panel);
36062 panel.tabItem = ti;
36067 updatePanelTitle : function(panel, title)
36069 if(this.activePanel == panel){
36070 this.updateTitle(title);
36073 var ti = this.tabs.getTab(panel.getEl().id);
36075 if(panel.tabTip !== undefined){
36076 ti.setTooltip(panel.tabTip);
36081 updateTitle : function(title){
36082 if(this.titleTextEl && !this.config.title){
36083 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36087 setActivePanel : function(panel)
36089 panel = this.getPanel(panel);
36090 if(this.activePanel && this.activePanel != panel){
36091 if(this.activePanel.setActiveState(false) === false){
36095 this.activePanel = panel;
36096 panel.setActiveState(true);
36097 if(this.panelSize){
36098 panel.setSize(this.panelSize.width, this.panelSize.height);
36101 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36103 this.updateTitle(panel.getTitle());
36105 this.fireEvent("invalidated", this);
36107 this.fireEvent("panelactivated", this, panel);
36111 * Shows the specified panel.
36112 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36113 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36115 showPanel : function(panel)
36117 panel = this.getPanel(panel);
36120 var tab = this.tabs.getTab(panel.getEl().id);
36121 if(tab.isHidden()){
36122 this.tabs.unhideTab(tab.id);
36126 this.setActivePanel(panel);
36133 * Get the active panel for this region.
36134 * @return {Roo.ContentPanel} The active panel or null
36136 getActivePanel : function(){
36137 return this.activePanel;
36140 validateVisibility : function(){
36141 if(this.panels.getCount() < 1){
36142 this.updateTitle(" ");
36143 this.closeBtn.hide();
36146 if(!this.isVisible()){
36153 * Adds the passed ContentPanel(s) to this region.
36154 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36155 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36157 add : function(panel)
36159 if(arguments.length > 1){
36160 for(var i = 0, len = arguments.length; i < len; i++) {
36161 this.add(arguments[i]);
36166 // if we have not been rendered yet, then we can not really do much of this..
36167 if (!this.bodyEl) {
36168 this.unrendered_panels.push(panel);
36175 if(this.hasPanel(panel)){
36176 this.showPanel(panel);
36179 panel.setRegion(this);
36180 this.panels.add(panel);
36181 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36182 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36183 // and hide them... ???
36184 this.bodyEl.dom.appendChild(panel.getEl().dom);
36185 if(panel.background !== true){
36186 this.setActivePanel(panel);
36188 this.fireEvent("paneladded", this, panel);
36195 this.initPanelAsTab(panel);
36199 if(panel.background !== true){
36200 this.tabs.activate(panel.getEl().id);
36202 this.fireEvent("paneladded", this, panel);
36207 * Hides the tab for the specified panel.
36208 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36210 hidePanel : function(panel){
36211 if(this.tabs && (panel = this.getPanel(panel))){
36212 this.tabs.hideTab(panel.getEl().id);
36217 * Unhides the tab for a previously hidden panel.
36218 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36220 unhidePanel : function(panel){
36221 if(this.tabs && (panel = this.getPanel(panel))){
36222 this.tabs.unhideTab(panel.getEl().id);
36226 clearPanels : function(){
36227 while(this.panels.getCount() > 0){
36228 this.remove(this.panels.first());
36233 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36234 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36235 * @param {Boolean} preservePanel Overrides the config preservePanel option
36236 * @return {Roo.ContentPanel} The panel that was removed
36238 remove : function(panel, preservePanel)
36240 panel = this.getPanel(panel);
36245 this.fireEvent("beforeremove", this, panel, e);
36246 if(e.cancel === true){
36249 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36250 var panelId = panel.getId();
36251 this.panels.removeKey(panelId);
36253 document.body.appendChild(panel.getEl().dom);
36256 this.tabs.removeTab(panel.getEl().id);
36257 }else if (!preservePanel){
36258 this.bodyEl.dom.removeChild(panel.getEl().dom);
36260 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36261 var p = this.panels.first();
36262 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36263 tempEl.appendChild(p.getEl().dom);
36264 this.bodyEl.update("");
36265 this.bodyEl.dom.appendChild(p.getEl().dom);
36267 this.updateTitle(p.getTitle());
36269 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36270 this.setActivePanel(p);
36272 panel.setRegion(null);
36273 if(this.activePanel == panel){
36274 this.activePanel = null;
36276 if(this.config.autoDestroy !== false && preservePanel !== true){
36277 try{panel.destroy();}catch(e){}
36279 this.fireEvent("panelremoved", this, panel);
36284 * Returns the TabPanel component used by this region
36285 * @return {Roo.TabPanel}
36287 getTabs : function(){
36291 createTool : function(parentEl, className){
36292 var btn = Roo.DomHelper.append(parentEl, {
36294 cls: "x-layout-tools-button",
36297 cls: "roo-layout-tools-button-inner " + className,
36301 btn.addClassOnOver("roo-layout-tools-button-over");
36306 * Ext JS Library 1.1.1
36307 * Copyright(c) 2006-2007, Ext JS, LLC.
36309 * Originally Released Under LGPL - original licence link has changed is not relivant.
36312 * <script type="text/javascript">
36318 * @class Roo.SplitLayoutRegion
36319 * @extends Roo.LayoutRegion
36320 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36322 Roo.bootstrap.layout.Split = function(config){
36323 this.cursor = config.cursor;
36324 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36327 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36329 splitTip : "Drag to resize.",
36330 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36331 useSplitTips : false,
36333 applyConfig : function(config){
36334 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36337 onRender : function(ctr,pos) {
36339 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36340 if(!this.config.split){
36345 var splitEl = Roo.DomHelper.append(ctr.dom, {
36347 id: this.el.id + "-split",
36348 cls: "roo-layout-split roo-layout-split-"+this.position,
36351 /** The SplitBar for this region
36352 * @type Roo.SplitBar */
36353 // does not exist yet...
36354 Roo.log([this.position, this.orientation]);
36356 this.split = new Roo.bootstrap.SplitBar({
36357 dragElement : splitEl,
36358 resizingElement: this.el,
36359 orientation : this.orientation
36362 this.split.on("moved", this.onSplitMove, this);
36363 this.split.useShim = this.config.useShim === true;
36364 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36365 if(this.useSplitTips){
36366 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36368 //if(config.collapsible){
36369 // this.split.el.on("dblclick", this.collapse, this);
36372 if(typeof this.config.minSize != "undefined"){
36373 this.split.minSize = this.config.minSize;
36375 if(typeof this.config.maxSize != "undefined"){
36376 this.split.maxSize = this.config.maxSize;
36378 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36379 this.hideSplitter();
36384 getHMaxSize : function(){
36385 var cmax = this.config.maxSize || 10000;
36386 var center = this.mgr.getRegion("center");
36387 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36390 getVMaxSize : function(){
36391 var cmax = this.config.maxSize || 10000;
36392 var center = this.mgr.getRegion("center");
36393 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36396 onSplitMove : function(split, newSize){
36397 this.fireEvent("resized", this, newSize);
36401 * Returns the {@link Roo.SplitBar} for this region.
36402 * @return {Roo.SplitBar}
36404 getSplitBar : function(){
36409 this.hideSplitter();
36410 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36413 hideSplitter : function(){
36415 this.split.el.setLocation(-2000,-2000);
36416 this.split.el.hide();
36422 this.split.el.show();
36424 Roo.bootstrap.layout.Split.superclass.show.call(this);
36427 beforeSlide: function(){
36428 if(Roo.isGecko){// firefox overflow auto bug workaround
36429 this.bodyEl.clip();
36431 this.tabs.bodyEl.clip();
36433 if(this.activePanel){
36434 this.activePanel.getEl().clip();
36436 if(this.activePanel.beforeSlide){
36437 this.activePanel.beforeSlide();
36443 afterSlide : function(){
36444 if(Roo.isGecko){// firefox overflow auto bug workaround
36445 this.bodyEl.unclip();
36447 this.tabs.bodyEl.unclip();
36449 if(this.activePanel){
36450 this.activePanel.getEl().unclip();
36451 if(this.activePanel.afterSlide){
36452 this.activePanel.afterSlide();
36458 initAutoHide : function(){
36459 if(this.autoHide !== false){
36460 if(!this.autoHideHd){
36461 var st = new Roo.util.DelayedTask(this.slideIn, this);
36462 this.autoHideHd = {
36463 "mouseout": function(e){
36464 if(!e.within(this.el, true)){
36468 "mouseover" : function(e){
36474 this.el.on(this.autoHideHd);
36478 clearAutoHide : function(){
36479 if(this.autoHide !== false){
36480 this.el.un("mouseout", this.autoHideHd.mouseout);
36481 this.el.un("mouseover", this.autoHideHd.mouseover);
36485 clearMonitor : function(){
36486 Roo.get(document).un("click", this.slideInIf, this);
36489 // these names are backwards but not changed for compat
36490 slideOut : function(){
36491 if(this.isSlid || this.el.hasActiveFx()){
36494 this.isSlid = true;
36495 if(this.collapseBtn){
36496 this.collapseBtn.hide();
36498 this.closeBtnState = this.closeBtn.getStyle('display');
36499 this.closeBtn.hide();
36501 this.stickBtn.show();
36504 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36505 this.beforeSlide();
36506 this.el.setStyle("z-index", 10001);
36507 this.el.slideIn(this.getSlideAnchor(), {
36508 callback: function(){
36510 this.initAutoHide();
36511 Roo.get(document).on("click", this.slideInIf, this);
36512 this.fireEvent("slideshow", this);
36519 afterSlideIn : function(){
36520 this.clearAutoHide();
36521 this.isSlid = false;
36522 this.clearMonitor();
36523 this.el.setStyle("z-index", "");
36524 if(this.collapseBtn){
36525 this.collapseBtn.show();
36527 this.closeBtn.setStyle('display', this.closeBtnState);
36529 this.stickBtn.hide();
36531 this.fireEvent("slidehide", this);
36534 slideIn : function(cb){
36535 if(!this.isSlid || this.el.hasActiveFx()){
36539 this.isSlid = false;
36540 this.beforeSlide();
36541 this.el.slideOut(this.getSlideAnchor(), {
36542 callback: function(){
36543 this.el.setLeftTop(-10000, -10000);
36545 this.afterSlideIn();
36553 slideInIf : function(e){
36554 if(!e.within(this.el)){
36559 animateCollapse : function(){
36560 this.beforeSlide();
36561 this.el.setStyle("z-index", 20000);
36562 var anchor = this.getSlideAnchor();
36563 this.el.slideOut(anchor, {
36564 callback : function(){
36565 this.el.setStyle("z-index", "");
36566 this.collapsedEl.slideIn(anchor, {duration:.3});
36568 this.el.setLocation(-10000,-10000);
36570 this.fireEvent("collapsed", this);
36577 animateExpand : function(){
36578 this.beforeSlide();
36579 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36580 this.el.setStyle("z-index", 20000);
36581 this.collapsedEl.hide({
36584 this.el.slideIn(this.getSlideAnchor(), {
36585 callback : function(){
36586 this.el.setStyle("z-index", "");
36589 this.split.el.show();
36591 this.fireEvent("invalidated", this);
36592 this.fireEvent("expanded", this);
36620 getAnchor : function(){
36621 return this.anchors[this.position];
36624 getCollapseAnchor : function(){
36625 return this.canchors[this.position];
36628 getSlideAnchor : function(){
36629 return this.sanchors[this.position];
36632 getAlignAdj : function(){
36633 var cm = this.cmargins;
36634 switch(this.position){
36650 getExpandAdj : function(){
36651 var c = this.collapsedEl, cm = this.cmargins;
36652 switch(this.position){
36654 return [-(cm.right+c.getWidth()+cm.left), 0];
36657 return [cm.right+c.getWidth()+cm.left, 0];
36660 return [0, -(cm.top+cm.bottom+c.getHeight())];
36663 return [0, cm.top+cm.bottom+c.getHeight()];
36669 * Ext JS Library 1.1.1
36670 * Copyright(c) 2006-2007, Ext JS, LLC.
36672 * Originally Released Under LGPL - original licence link has changed is not relivant.
36675 * <script type="text/javascript">
36678 * These classes are private internal classes
36680 Roo.bootstrap.layout.Center = function(config){
36681 config.region = "center";
36682 Roo.bootstrap.layout.Region.call(this, config);
36683 this.visible = true;
36684 this.minWidth = config.minWidth || 20;
36685 this.minHeight = config.minHeight || 20;
36688 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36690 // center panel can't be hidden
36694 // center panel can't be hidden
36697 getMinWidth: function(){
36698 return this.minWidth;
36701 getMinHeight: function(){
36702 return this.minHeight;
36715 Roo.bootstrap.layout.North = function(config)
36717 config.region = 'north';
36718 config.cursor = 'n-resize';
36720 Roo.bootstrap.layout.Split.call(this, config);
36724 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36725 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36726 this.split.el.addClass("roo-layout-split-v");
36728 var size = config.initialSize || config.height;
36729 if(typeof size != "undefined"){
36730 this.el.setHeight(size);
36733 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36735 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36739 getBox : function(){
36740 if(this.collapsed){
36741 return this.collapsedEl.getBox();
36743 var box = this.el.getBox();
36745 box.height += this.split.el.getHeight();
36750 updateBox : function(box){
36751 if(this.split && !this.collapsed){
36752 box.height -= this.split.el.getHeight();
36753 this.split.el.setLeft(box.x);
36754 this.split.el.setTop(box.y+box.height);
36755 this.split.el.setWidth(box.width);
36757 if(this.collapsed){
36758 this.updateBody(box.width, null);
36760 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36768 Roo.bootstrap.layout.South = function(config){
36769 config.region = 'south';
36770 config.cursor = 's-resize';
36771 Roo.bootstrap.layout.Split.call(this, config);
36773 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36774 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36775 this.split.el.addClass("roo-layout-split-v");
36777 var size = config.initialSize || config.height;
36778 if(typeof size != "undefined"){
36779 this.el.setHeight(size);
36783 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36784 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36785 getBox : function(){
36786 if(this.collapsed){
36787 return this.collapsedEl.getBox();
36789 var box = this.el.getBox();
36791 var sh = this.split.el.getHeight();
36798 updateBox : function(box){
36799 if(this.split && !this.collapsed){
36800 var sh = this.split.el.getHeight();
36803 this.split.el.setLeft(box.x);
36804 this.split.el.setTop(box.y-sh);
36805 this.split.el.setWidth(box.width);
36807 if(this.collapsed){
36808 this.updateBody(box.width, null);
36810 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36814 Roo.bootstrap.layout.East = function(config){
36815 config.region = "east";
36816 config.cursor = "e-resize";
36817 Roo.bootstrap.layout.Split.call(this, config);
36819 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36820 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36821 this.split.el.addClass("roo-layout-split-h");
36823 var size = config.initialSize || config.width;
36824 if(typeof size != "undefined"){
36825 this.el.setWidth(size);
36828 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36829 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36830 getBox : function(){
36831 if(this.collapsed){
36832 return this.collapsedEl.getBox();
36834 var box = this.el.getBox();
36836 var sw = this.split.el.getWidth();
36843 updateBox : function(box){
36844 if(this.split && !this.collapsed){
36845 var sw = this.split.el.getWidth();
36847 this.split.el.setLeft(box.x);
36848 this.split.el.setTop(box.y);
36849 this.split.el.setHeight(box.height);
36852 if(this.collapsed){
36853 this.updateBody(null, box.height);
36855 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36859 Roo.bootstrap.layout.West = function(config){
36860 config.region = "west";
36861 config.cursor = "w-resize";
36863 Roo.bootstrap.layout.Split.call(this, config);
36865 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36866 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36867 this.split.el.addClass("roo-layout-split-h");
36871 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36872 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36874 onRender: function(ctr, pos)
36876 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36877 var size = this.config.initialSize || this.config.width;
36878 if(typeof size != "undefined"){
36879 this.el.setWidth(size);
36883 getBox : function(){
36884 if(this.collapsed){
36885 return this.collapsedEl.getBox();
36887 var box = this.el.getBox();
36889 box.width += this.split.el.getWidth();
36894 updateBox : function(box){
36895 if(this.split && !this.collapsed){
36896 var sw = this.split.el.getWidth();
36898 this.split.el.setLeft(box.x+box.width);
36899 this.split.el.setTop(box.y);
36900 this.split.el.setHeight(box.height);
36902 if(this.collapsed){
36903 this.updateBody(null, box.height);
36905 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36908 Roo.namespace("Roo.bootstrap.panel");/*
36910 * Ext JS Library 1.1.1
36911 * Copyright(c) 2006-2007, Ext JS, LLC.
36913 * Originally Released Under LGPL - original licence link has changed is not relivant.
36916 * <script type="text/javascript">
36919 * @class Roo.ContentPanel
36920 * @extends Roo.util.Observable
36921 * A basic ContentPanel element.
36922 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36923 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36924 * @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
36925 * @cfg {Boolean} closable True if the panel can be closed/removed
36926 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36927 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36928 * @cfg {Toolbar} toolbar A toolbar for this panel
36929 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36930 * @cfg {String} title The title for this panel
36931 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36932 * @cfg {String} url Calls {@link #setUrl} with this value
36933 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36934 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36935 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36936 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36937 * @cfg {Boolean} badges render the badges
36940 * Create a new ContentPanel.
36941 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36942 * @param {String/Object} config A string to set only the title or a config object
36943 * @param {String} content (optional) Set the HTML content for this panel
36944 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36946 Roo.bootstrap.panel.Content = function( config){
36948 this.tpl = config.tpl || false;
36950 var el = config.el;
36951 var content = config.content;
36953 if(config.autoCreate){ // xtype is available if this is called from factory
36956 this.el = Roo.get(el);
36957 if(!this.el && config && config.autoCreate){
36958 if(typeof config.autoCreate == "object"){
36959 if(!config.autoCreate.id){
36960 config.autoCreate.id = config.id||el;
36962 this.el = Roo.DomHelper.append(document.body,
36963 config.autoCreate, true);
36965 var elcfg = { tag: "div",
36966 cls: "roo-layout-inactive-content",
36970 elcfg.html = config.html;
36974 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36977 this.closable = false;
36978 this.loaded = false;
36979 this.active = false;
36982 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36984 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36986 this.wrapEl = this.el; //this.el.wrap();
36988 if (config.toolbar.items) {
36989 ti = config.toolbar.items ;
36990 delete config.toolbar.items ;
36994 this.toolbar.render(this.wrapEl, 'before');
36995 for(var i =0;i < ti.length;i++) {
36996 // Roo.log(['add child', items[i]]);
36997 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36999 this.toolbar.items = nitems;
37000 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37001 delete config.toolbar;
37005 // xtype created footer. - not sure if will work as we normally have to render first..
37006 if (this.footer && !this.footer.el && this.footer.xtype) {
37007 if (!this.wrapEl) {
37008 this.wrapEl = this.el.wrap();
37011 this.footer.container = this.wrapEl.createChild();
37013 this.footer = Roo.factory(this.footer, Roo);
37018 if(typeof config == "string"){
37019 this.title = config;
37021 Roo.apply(this, config);
37025 this.resizeEl = Roo.get(this.resizeEl, true);
37027 this.resizeEl = this.el;
37029 // handle view.xtype
37037 * Fires when this panel is activated.
37038 * @param {Roo.ContentPanel} this
37042 * @event deactivate
37043 * Fires when this panel is activated.
37044 * @param {Roo.ContentPanel} this
37046 "deactivate" : true,
37050 * Fires when this panel is resized if fitToFrame is true.
37051 * @param {Roo.ContentPanel} this
37052 * @param {Number} width The width after any component adjustments
37053 * @param {Number} height The height after any component adjustments
37059 * Fires when this tab is created
37060 * @param {Roo.ContentPanel} this
37071 if(this.autoScroll){
37072 this.resizeEl.setStyle("overflow", "auto");
37074 // fix randome scrolling
37075 //this.el.on('scroll', function() {
37076 // Roo.log('fix random scolling');
37077 // this.scrollTo('top',0);
37080 content = content || this.content;
37082 this.setContent(content);
37084 if(config && config.url){
37085 this.setUrl(this.url, this.params, this.loadOnce);
37090 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37092 if (this.view && typeof(this.view.xtype) != 'undefined') {
37093 this.view.el = this.el.appendChild(document.createElement("div"));
37094 this.view = Roo.factory(this.view);
37095 this.view.render && this.view.render(false, '');
37099 this.fireEvent('render', this);
37102 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37106 setRegion : function(region){
37107 this.region = region;
37108 this.setActiveClass(region && !this.background);
37112 setActiveClass: function(state)
37115 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37116 this.el.setStyle('position','relative');
37118 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37119 this.el.setStyle('position', 'absolute');
37124 * Returns the toolbar for this Panel if one was configured.
37125 * @return {Roo.Toolbar}
37127 getToolbar : function(){
37128 return this.toolbar;
37131 setActiveState : function(active)
37133 this.active = active;
37134 this.setActiveClass(active);
37136 if(this.fireEvent("deactivate", this) === false){
37141 this.fireEvent("activate", this);
37145 * Updates this panel's element
37146 * @param {String} content The new content
37147 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37149 setContent : function(content, loadScripts){
37150 this.el.update(content, loadScripts);
37153 ignoreResize : function(w, h){
37154 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37157 this.lastSize = {width: w, height: h};
37162 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37163 * @return {Roo.UpdateManager} The UpdateManager
37165 getUpdateManager : function(){
37166 return this.el.getUpdateManager();
37169 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37170 * @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:
37173 url: "your-url.php",
37174 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37175 callback: yourFunction,
37176 scope: yourObject, //(optional scope)
37179 text: "Loading...",
37184 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37185 * 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.
37186 * @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}
37187 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37188 * @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.
37189 * @return {Roo.ContentPanel} this
37192 var um = this.el.getUpdateManager();
37193 um.update.apply(um, arguments);
37199 * 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.
37200 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37201 * @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)
37202 * @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)
37203 * @return {Roo.UpdateManager} The UpdateManager
37205 setUrl : function(url, params, loadOnce){
37206 if(this.refreshDelegate){
37207 this.removeListener("activate", this.refreshDelegate);
37209 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37210 this.on("activate", this.refreshDelegate);
37211 return this.el.getUpdateManager();
37214 _handleRefresh : function(url, params, loadOnce){
37215 if(!loadOnce || !this.loaded){
37216 var updater = this.el.getUpdateManager();
37217 updater.update(url, params, this._setLoaded.createDelegate(this));
37221 _setLoaded : function(){
37222 this.loaded = true;
37226 * Returns this panel's id
37229 getId : function(){
37234 * Returns this panel's element - used by regiosn to add.
37235 * @return {Roo.Element}
37237 getEl : function(){
37238 return this.wrapEl || this.el;
37243 adjustForComponents : function(width, height)
37245 //Roo.log('adjustForComponents ');
37246 if(this.resizeEl != this.el){
37247 width -= this.el.getFrameWidth('lr');
37248 height -= this.el.getFrameWidth('tb');
37251 var te = this.toolbar.getEl();
37252 te.setWidth(width);
37253 height -= te.getHeight();
37256 var te = this.footer.getEl();
37257 te.setWidth(width);
37258 height -= te.getHeight();
37262 if(this.adjustments){
37263 width += this.adjustments[0];
37264 height += this.adjustments[1];
37266 return {"width": width, "height": height};
37269 setSize : function(width, height){
37270 if(this.fitToFrame && !this.ignoreResize(width, height)){
37271 if(this.fitContainer && this.resizeEl != this.el){
37272 this.el.setSize(width, height);
37274 var size = this.adjustForComponents(width, height);
37275 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37276 this.fireEvent('resize', this, size.width, size.height);
37281 * Returns this panel's title
37284 getTitle : function(){
37286 if (typeof(this.title) != 'object') {
37291 for (var k in this.title) {
37292 if (!this.title.hasOwnProperty(k)) {
37296 if (k.indexOf('-') >= 0) {
37297 var s = k.split('-');
37298 for (var i = 0; i<s.length; i++) {
37299 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37302 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37309 * Set this panel's title
37310 * @param {String} title
37312 setTitle : function(title){
37313 this.title = title;
37315 this.region.updatePanelTitle(this, title);
37320 * Returns true is this panel was configured to be closable
37321 * @return {Boolean}
37323 isClosable : function(){
37324 return this.closable;
37327 beforeSlide : function(){
37329 this.resizeEl.clip();
37332 afterSlide : function(){
37334 this.resizeEl.unclip();
37338 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37339 * Will fail silently if the {@link #setUrl} method has not been called.
37340 * This does not activate the panel, just updates its content.
37342 refresh : function(){
37343 if(this.refreshDelegate){
37344 this.loaded = false;
37345 this.refreshDelegate();
37350 * Destroys this panel
37352 destroy : function(){
37353 this.el.removeAllListeners();
37354 var tempEl = document.createElement("span");
37355 tempEl.appendChild(this.el.dom);
37356 tempEl.innerHTML = "";
37362 * form - if the content panel contains a form - this is a reference to it.
37363 * @type {Roo.form.Form}
37367 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37368 * This contains a reference to it.
37374 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37384 * @param {Object} cfg Xtype definition of item to add.
37388 getChildContainer: function () {
37389 return this.getEl();
37394 var ret = new Roo.factory(cfg);
37399 if (cfg.xtype.match(/^Form$/)) {
37402 //if (this.footer) {
37403 // el = this.footer.container.insertSibling(false, 'before');
37405 el = this.el.createChild();
37408 this.form = new Roo.form.Form(cfg);
37411 if ( this.form.allItems.length) {
37412 this.form.render(el.dom);
37416 // should only have one of theses..
37417 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37418 // views.. should not be just added - used named prop 'view''
37420 cfg.el = this.el.appendChild(document.createElement("div"));
37423 var ret = new Roo.factory(cfg);
37425 ret.render && ret.render(false, ''); // render blank..
37435 * @class Roo.bootstrap.panel.Grid
37436 * @extends Roo.bootstrap.panel.Content
37438 * Create a new GridPanel.
37439 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37440 * @param {Object} config A the config object
37446 Roo.bootstrap.panel.Grid = function(config)
37450 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37451 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37453 config.el = this.wrapper;
37454 //this.el = this.wrapper;
37456 if (config.container) {
37457 // ctor'ed from a Border/panel.grid
37460 this.wrapper.setStyle("overflow", "hidden");
37461 this.wrapper.addClass('roo-grid-container');
37466 if(config.toolbar){
37467 var tool_el = this.wrapper.createChild();
37468 this.toolbar = Roo.factory(config.toolbar);
37470 if (config.toolbar.items) {
37471 ti = config.toolbar.items ;
37472 delete config.toolbar.items ;
37476 this.toolbar.render(tool_el);
37477 for(var i =0;i < ti.length;i++) {
37478 // Roo.log(['add child', items[i]]);
37479 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37481 this.toolbar.items = nitems;
37483 delete config.toolbar;
37486 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37487 config.grid.scrollBody = true;;
37488 config.grid.monitorWindowResize = false; // turn off autosizing
37489 config.grid.autoHeight = false;
37490 config.grid.autoWidth = false;
37492 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37494 if (config.background) {
37495 // render grid on panel activation (if panel background)
37496 this.on('activate', function(gp) {
37497 if (!gp.grid.rendered) {
37498 gp.grid.render(this.wrapper);
37499 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37504 this.grid.render(this.wrapper);
37505 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37508 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37509 // ??? needed ??? config.el = this.wrapper;
37514 // xtype created footer. - not sure if will work as we normally have to render first..
37515 if (this.footer && !this.footer.el && this.footer.xtype) {
37517 var ctr = this.grid.getView().getFooterPanel(true);
37518 this.footer.dataSource = this.grid.dataSource;
37519 this.footer = Roo.factory(this.footer, Roo);
37520 this.footer.render(ctr);
37530 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37531 getId : function(){
37532 return this.grid.id;
37536 * Returns the grid for this panel
37537 * @return {Roo.bootstrap.Table}
37539 getGrid : function(){
37543 setSize : function(width, height){
37544 if(!this.ignoreResize(width, height)){
37545 var grid = this.grid;
37546 var size = this.adjustForComponents(width, height);
37547 var gridel = grid.getGridEl();
37548 gridel.setSize(size.width, size.height);
37550 var thd = grid.getGridEl().select('thead',true).first();
37551 var tbd = grid.getGridEl().select('tbody', true).first();
37553 tbd.setSize(width, height - thd.getHeight());
37562 beforeSlide : function(){
37563 this.grid.getView().scroller.clip();
37566 afterSlide : function(){
37567 this.grid.getView().scroller.unclip();
37570 destroy : function(){
37571 this.grid.destroy();
37573 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37578 * @class Roo.bootstrap.panel.Nest
37579 * @extends Roo.bootstrap.panel.Content
37581 * Create a new Panel, that can contain a layout.Border.
37584 * @param {Roo.BorderLayout} layout The layout for this panel
37585 * @param {String/Object} config A string to set only the title or a config object
37587 Roo.bootstrap.panel.Nest = function(config)
37589 // construct with only one argument..
37590 /* FIXME - implement nicer consturctors
37591 if (layout.layout) {
37593 layout = config.layout;
37594 delete config.layout;
37596 if (layout.xtype && !layout.getEl) {
37597 // then layout needs constructing..
37598 layout = Roo.factory(layout, Roo);
37602 config.el = config.layout.getEl();
37604 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37606 config.layout.monitorWindowResize = false; // turn off autosizing
37607 this.layout = config.layout;
37608 this.layout.getEl().addClass("roo-layout-nested-layout");
37615 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37617 setSize : function(width, height){
37618 if(!this.ignoreResize(width, height)){
37619 var size = this.adjustForComponents(width, height);
37620 var el = this.layout.getEl();
37621 if (size.height < 1) {
37622 el.setWidth(size.width);
37624 el.setSize(size.width, size.height);
37626 var touch = el.dom.offsetWidth;
37627 this.layout.layout();
37628 // ie requires a double layout on the first pass
37629 if(Roo.isIE && !this.initialized){
37630 this.initialized = true;
37631 this.layout.layout();
37636 // activate all subpanels if not currently active..
37638 setActiveState : function(active){
37639 this.active = active;
37640 this.setActiveClass(active);
37643 this.fireEvent("deactivate", this);
37647 this.fireEvent("activate", this);
37648 // not sure if this should happen before or after..
37649 if (!this.layout) {
37650 return; // should not happen..
37653 for (var r in this.layout.regions) {
37654 reg = this.layout.getRegion(r);
37655 if (reg.getActivePanel()) {
37656 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37657 reg.setActivePanel(reg.getActivePanel());
37660 if (!reg.panels.length) {
37663 reg.showPanel(reg.getPanel(0));
37672 * Returns the nested BorderLayout for this panel
37673 * @return {Roo.BorderLayout}
37675 getLayout : function(){
37676 return this.layout;
37680 * Adds a xtype elements to the layout of the nested panel
37684 xtype : 'ContentPanel',
37691 xtype : 'NestedLayoutPanel',
37697 items : [ ... list of content panels or nested layout panels.. ]
37701 * @param {Object} cfg Xtype definition of item to add.
37703 addxtype : function(cfg) {
37704 return this.layout.addxtype(cfg);
37709 * Ext JS Library 1.1.1
37710 * Copyright(c) 2006-2007, Ext JS, LLC.
37712 * Originally Released Under LGPL - original licence link has changed is not relivant.
37715 * <script type="text/javascript">
37718 * @class Roo.TabPanel
37719 * @extends Roo.util.Observable
37720 * A lightweight tab container.
37724 // basic tabs 1, built from existing content
37725 var tabs = new Roo.TabPanel("tabs1");
37726 tabs.addTab("script", "View Script");
37727 tabs.addTab("markup", "View Markup");
37728 tabs.activate("script");
37730 // more advanced tabs, built from javascript
37731 var jtabs = new Roo.TabPanel("jtabs");
37732 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37734 // set up the UpdateManager
37735 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37736 var updater = tab2.getUpdateManager();
37737 updater.setDefaultUrl("ajax1.htm");
37738 tab2.on('activate', updater.refresh, updater, true);
37740 // Use setUrl for Ajax loading
37741 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37742 tab3.setUrl("ajax2.htm", null, true);
37745 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37748 jtabs.activate("jtabs-1");
37751 * Create a new TabPanel.
37752 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37753 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37755 Roo.bootstrap.panel.Tabs = function(config){
37757 * The container element for this TabPanel.
37758 * @type Roo.Element
37760 this.el = Roo.get(config.el);
37763 if(typeof config == "boolean"){
37764 this.tabPosition = config ? "bottom" : "top";
37766 Roo.apply(this, config);
37770 if(this.tabPosition == "bottom"){
37771 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37772 this.el.addClass("roo-tabs-bottom");
37774 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37775 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37776 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37778 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37780 if(this.tabPosition != "bottom"){
37781 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37782 * @type Roo.Element
37784 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37785 this.el.addClass("roo-tabs-top");
37789 this.bodyEl.setStyle("position", "relative");
37791 this.active = null;
37792 this.activateDelegate = this.activate.createDelegate(this);
37797 * Fires when the active tab changes
37798 * @param {Roo.TabPanel} this
37799 * @param {Roo.TabPanelItem} activePanel The new active tab
37803 * @event beforetabchange
37804 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37805 * @param {Roo.TabPanel} this
37806 * @param {Object} e Set cancel to true on this object to cancel the tab change
37807 * @param {Roo.TabPanelItem} tab The tab being changed to
37809 "beforetabchange" : true
37812 Roo.EventManager.onWindowResize(this.onResize, this);
37813 this.cpad = this.el.getPadding("lr");
37814 this.hiddenCount = 0;
37817 // toolbar on the tabbar support...
37818 if (this.toolbar) {
37819 alert("no toolbar support yet");
37820 this.toolbar = false;
37822 var tcfg = this.toolbar;
37823 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37824 this.toolbar = new Roo.Toolbar(tcfg);
37825 if (Roo.isSafari) {
37826 var tbl = tcfg.container.child('table', true);
37827 tbl.setAttribute('width', '100%');
37835 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37838 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37840 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37842 tabPosition : "top",
37844 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37846 currentTabWidth : 0,
37848 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37852 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37856 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37858 preferredTabWidth : 175,
37860 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37862 resizeTabs : false,
37864 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37866 monitorResize : true,
37868 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37873 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37874 * @param {String} id The id of the div to use <b>or create</b>
37875 * @param {String} text The text for the tab
37876 * @param {String} content (optional) Content to put in the TabPanelItem body
37877 * @param {Boolean} closable (optional) True to create a close icon on the tab
37878 * @return {Roo.TabPanelItem} The created TabPanelItem
37880 addTab : function(id, text, content, closable, tpl)
37882 var item = new Roo.bootstrap.panel.TabItem({
37886 closable : closable,
37889 this.addTabItem(item);
37891 item.setContent(content);
37897 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37898 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37899 * @return {Roo.TabPanelItem}
37901 getTab : function(id){
37902 return this.items[id];
37906 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37907 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37909 hideTab : function(id){
37910 var t = this.items[id];
37913 this.hiddenCount++;
37914 this.autoSizeTabs();
37919 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37920 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37922 unhideTab : function(id){
37923 var t = this.items[id];
37925 t.setHidden(false);
37926 this.hiddenCount--;
37927 this.autoSizeTabs();
37932 * Adds an existing {@link Roo.TabPanelItem}.
37933 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37935 addTabItem : function(item){
37936 this.items[item.id] = item;
37937 this.items.push(item);
37938 // if(this.resizeTabs){
37939 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37940 // this.autoSizeTabs();
37942 // item.autoSize();
37947 * Removes a {@link Roo.TabPanelItem}.
37948 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37950 removeTab : function(id){
37951 var items = this.items;
37952 var tab = items[id];
37953 if(!tab) { return; }
37954 var index = items.indexOf(tab);
37955 if(this.active == tab && items.length > 1){
37956 var newTab = this.getNextAvailable(index);
37961 this.stripEl.dom.removeChild(tab.pnode.dom);
37962 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37963 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37965 items.splice(index, 1);
37966 delete this.items[tab.id];
37967 tab.fireEvent("close", tab);
37968 tab.purgeListeners();
37969 this.autoSizeTabs();
37972 getNextAvailable : function(start){
37973 var items = this.items;
37975 // look for a next tab that will slide over to
37976 // replace the one being removed
37977 while(index < items.length){
37978 var item = items[++index];
37979 if(item && !item.isHidden()){
37983 // if one isn't found select the previous tab (on the left)
37986 var item = items[--index];
37987 if(item && !item.isHidden()){
37995 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37996 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37998 disableTab : function(id){
37999 var tab = this.items[id];
38000 if(tab && this.active != tab){
38006 * Enables a {@link Roo.TabPanelItem} that is disabled.
38007 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38009 enableTab : function(id){
38010 var tab = this.items[id];
38015 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38016 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38017 * @return {Roo.TabPanelItem} The TabPanelItem.
38019 activate : function(id){
38020 var tab = this.items[id];
38024 if(tab == this.active || tab.disabled){
38028 this.fireEvent("beforetabchange", this, e, tab);
38029 if(e.cancel !== true && !tab.disabled){
38031 this.active.hide();
38033 this.active = this.items[id];
38034 this.active.show();
38035 this.fireEvent("tabchange", this, this.active);
38041 * Gets the active {@link Roo.TabPanelItem}.
38042 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38044 getActiveTab : function(){
38045 return this.active;
38049 * Updates the tab body element to fit the height of the container element
38050 * for overflow scrolling
38051 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38053 syncHeight : function(targetHeight){
38054 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38055 var bm = this.bodyEl.getMargins();
38056 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38057 this.bodyEl.setHeight(newHeight);
38061 onResize : function(){
38062 if(this.monitorResize){
38063 this.autoSizeTabs();
38068 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38070 beginUpdate : function(){
38071 this.updating = true;
38075 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38077 endUpdate : function(){
38078 this.updating = false;
38079 this.autoSizeTabs();
38083 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38085 autoSizeTabs : function(){
38086 var count = this.items.length;
38087 var vcount = count - this.hiddenCount;
38088 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38091 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38092 var availWidth = Math.floor(w / vcount);
38093 var b = this.stripBody;
38094 if(b.getWidth() > w){
38095 var tabs = this.items;
38096 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38097 if(availWidth < this.minTabWidth){
38098 /*if(!this.sleft){ // incomplete scrolling code
38099 this.createScrollButtons();
38102 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38105 if(this.currentTabWidth < this.preferredTabWidth){
38106 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38112 * Returns the number of tabs in this TabPanel.
38115 getCount : function(){
38116 return this.items.length;
38120 * Resizes all the tabs to the passed width
38121 * @param {Number} The new width
38123 setTabWidth : function(width){
38124 this.currentTabWidth = width;
38125 for(var i = 0, len = this.items.length; i < len; i++) {
38126 if(!this.items[i].isHidden()) {
38127 this.items[i].setWidth(width);
38133 * Destroys this TabPanel
38134 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38136 destroy : function(removeEl){
38137 Roo.EventManager.removeResizeListener(this.onResize, this);
38138 for(var i = 0, len = this.items.length; i < len; i++){
38139 this.items[i].purgeListeners();
38141 if(removeEl === true){
38142 this.el.update("");
38147 createStrip : function(container)
38149 var strip = document.createElement("nav");
38150 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38151 container.appendChild(strip);
38155 createStripList : function(strip)
38157 // div wrapper for retard IE
38158 // returns the "tr" element.
38159 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38160 //'<div class="x-tabs-strip-wrap">'+
38161 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38162 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38163 return strip.firstChild; //.firstChild.firstChild.firstChild;
38165 createBody : function(container)
38167 var body = document.createElement("div");
38168 Roo.id(body, "tab-body");
38169 //Roo.fly(body).addClass("x-tabs-body");
38170 Roo.fly(body).addClass("tab-content");
38171 container.appendChild(body);
38174 createItemBody :function(bodyEl, id){
38175 var body = Roo.getDom(id);
38177 body = document.createElement("div");
38180 //Roo.fly(body).addClass("x-tabs-item-body");
38181 Roo.fly(body).addClass("tab-pane");
38182 bodyEl.insertBefore(body, bodyEl.firstChild);
38186 createStripElements : function(stripEl, text, closable, tpl)
38188 var td = document.createElement("li"); // was td..
38191 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38194 stripEl.appendChild(td);
38196 td.className = "x-tabs-closable";
38197 if(!this.closeTpl){
38198 this.closeTpl = new Roo.Template(
38199 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38200 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38201 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38204 var el = this.closeTpl.overwrite(td, {"text": text});
38205 var close = el.getElementsByTagName("div")[0];
38206 var inner = el.getElementsByTagName("em")[0];
38207 return {"el": el, "close": close, "inner": inner};
38210 // not sure what this is..
38211 // if(!this.tabTpl){
38212 //this.tabTpl = new Roo.Template(
38213 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38214 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38216 // this.tabTpl = new Roo.Template(
38217 // '<a href="#">' +
38218 // '<span unselectable="on"' +
38219 // (this.disableTooltips ? '' : ' title="{text}"') +
38220 // ' >{text}</span></a>'
38226 var template = tpl || this.tabTpl || false;
38230 template = new Roo.Template(
38232 '<span unselectable="on"' +
38233 (this.disableTooltips ? '' : ' title="{text}"') +
38234 ' >{text}</span></a>'
38238 switch (typeof(template)) {
38242 template = new Roo.Template(template);
38248 var el = template.overwrite(td, {"text": text});
38250 var inner = el.getElementsByTagName("span")[0];
38252 return {"el": el, "inner": inner};
38260 * @class Roo.TabPanelItem
38261 * @extends Roo.util.Observable
38262 * Represents an individual item (tab plus body) in a TabPanel.
38263 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38264 * @param {String} id The id of this TabPanelItem
38265 * @param {String} text The text for the tab of this TabPanelItem
38266 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38268 Roo.bootstrap.panel.TabItem = function(config){
38270 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38271 * @type Roo.TabPanel
38273 this.tabPanel = config.panel;
38275 * The id for this TabPanelItem
38278 this.id = config.id;
38280 this.disabled = false;
38282 this.text = config.text;
38284 this.loaded = false;
38285 this.closable = config.closable;
38288 * The body element for this TabPanelItem.
38289 * @type Roo.Element
38291 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38292 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38293 this.bodyEl.setStyle("display", "block");
38294 this.bodyEl.setStyle("zoom", "1");
38295 //this.hideAction();
38297 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38299 this.el = Roo.get(els.el);
38300 this.inner = Roo.get(els.inner, true);
38301 this.textEl = Roo.get(this.el.dom.firstChild, true);
38302 this.pnode = Roo.get(els.el.parentNode, true);
38303 // this.el.on("mousedown", this.onTabMouseDown, this);
38304 this.el.on("click", this.onTabClick, this);
38306 if(config.closable){
38307 var c = Roo.get(els.close, true);
38308 c.dom.title = this.closeText;
38309 c.addClassOnOver("close-over");
38310 c.on("click", this.closeClick, this);
38316 * Fires when this tab becomes the active tab.
38317 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38318 * @param {Roo.TabPanelItem} this
38322 * @event beforeclose
38323 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38324 * @param {Roo.TabPanelItem} this
38325 * @param {Object} e Set cancel to true on this object to cancel the close.
38327 "beforeclose": true,
38330 * Fires when this tab is closed.
38331 * @param {Roo.TabPanelItem} this
38335 * @event deactivate
38336 * Fires when this tab is no longer the active tab.
38337 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38338 * @param {Roo.TabPanelItem} this
38340 "deactivate" : true
38342 this.hidden = false;
38344 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38347 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38349 purgeListeners : function(){
38350 Roo.util.Observable.prototype.purgeListeners.call(this);
38351 this.el.removeAllListeners();
38354 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38357 this.pnode.addClass("active");
38360 this.tabPanel.stripWrap.repaint();
38362 this.fireEvent("activate", this.tabPanel, this);
38366 * Returns true if this tab is the active tab.
38367 * @return {Boolean}
38369 isActive : function(){
38370 return this.tabPanel.getActiveTab() == this;
38374 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38377 this.pnode.removeClass("active");
38379 this.fireEvent("deactivate", this.tabPanel, this);
38382 hideAction : function(){
38383 this.bodyEl.hide();
38384 this.bodyEl.setStyle("position", "absolute");
38385 this.bodyEl.setLeft("-20000px");
38386 this.bodyEl.setTop("-20000px");
38389 showAction : function(){
38390 this.bodyEl.setStyle("position", "relative");
38391 this.bodyEl.setTop("");
38392 this.bodyEl.setLeft("");
38393 this.bodyEl.show();
38397 * Set the tooltip for the tab.
38398 * @param {String} tooltip The tab's tooltip
38400 setTooltip : function(text){
38401 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38402 this.textEl.dom.qtip = text;
38403 this.textEl.dom.removeAttribute('title');
38405 this.textEl.dom.title = text;
38409 onTabClick : function(e){
38410 e.preventDefault();
38411 this.tabPanel.activate(this.id);
38414 onTabMouseDown : function(e){
38415 e.preventDefault();
38416 this.tabPanel.activate(this.id);
38419 getWidth : function(){
38420 return this.inner.getWidth();
38423 setWidth : function(width){
38424 var iwidth = width - this.pnode.getPadding("lr");
38425 this.inner.setWidth(iwidth);
38426 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38427 this.pnode.setWidth(width);
38431 * Show or hide the tab
38432 * @param {Boolean} hidden True to hide or false to show.
38434 setHidden : function(hidden){
38435 this.hidden = hidden;
38436 this.pnode.setStyle("display", hidden ? "none" : "");
38440 * Returns true if this tab is "hidden"
38441 * @return {Boolean}
38443 isHidden : function(){
38444 return this.hidden;
38448 * Returns the text for this tab
38451 getText : function(){
38455 autoSize : function(){
38456 //this.el.beginMeasure();
38457 this.textEl.setWidth(1);
38459 * #2804 [new] Tabs in Roojs
38460 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38462 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38463 //this.el.endMeasure();
38467 * Sets the text for the tab (Note: this also sets the tooltip text)
38468 * @param {String} text The tab's text and tooltip
38470 setText : function(text){
38472 this.textEl.update(text);
38473 this.setTooltip(text);
38474 //if(!this.tabPanel.resizeTabs){
38475 // this.autoSize();
38479 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38481 activate : function(){
38482 this.tabPanel.activate(this.id);
38486 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38488 disable : function(){
38489 if(this.tabPanel.active != this){
38490 this.disabled = true;
38491 this.pnode.addClass("disabled");
38496 * Enables this TabPanelItem if it was previously disabled.
38498 enable : function(){
38499 this.disabled = false;
38500 this.pnode.removeClass("disabled");
38504 * Sets the content for this TabPanelItem.
38505 * @param {String} content The content
38506 * @param {Boolean} loadScripts true to look for and load scripts
38508 setContent : function(content, loadScripts){
38509 this.bodyEl.update(content, loadScripts);
38513 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38514 * @return {Roo.UpdateManager} The UpdateManager
38516 getUpdateManager : function(){
38517 return this.bodyEl.getUpdateManager();
38521 * Set a URL to be used to load the content for this TabPanelItem.
38522 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38523 * @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)
38524 * @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)
38525 * @return {Roo.UpdateManager} The UpdateManager
38527 setUrl : function(url, params, loadOnce){
38528 if(this.refreshDelegate){
38529 this.un('activate', this.refreshDelegate);
38531 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38532 this.on("activate", this.refreshDelegate);
38533 return this.bodyEl.getUpdateManager();
38537 _handleRefresh : function(url, params, loadOnce){
38538 if(!loadOnce || !this.loaded){
38539 var updater = this.bodyEl.getUpdateManager();
38540 updater.update(url, params, this._setLoaded.createDelegate(this));
38545 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38546 * Will fail silently if the setUrl method has not been called.
38547 * This does not activate the panel, just updates its content.
38549 refresh : function(){
38550 if(this.refreshDelegate){
38551 this.loaded = false;
38552 this.refreshDelegate();
38557 _setLoaded : function(){
38558 this.loaded = true;
38562 closeClick : function(e){
38565 this.fireEvent("beforeclose", this, o);
38566 if(o.cancel !== true){
38567 this.tabPanel.removeTab(this.id);
38571 * The text displayed in the tooltip for the close icon.
38574 closeText : "Close this tab"
38577 * This script refer to:
38578 * Title: International Telephone Input
38579 * Author: Jack O'Connor
38580 * Code version: v12.1.12
38581 * Availability: https://github.com/jackocnr/intl-tel-input.git
38584 Roo.bootstrap.PhoneInputData = function() {
38587 "Afghanistan (افغانستان)",
38592 "Albania (Shqipëri)",
38597 "Algeria (الجزائر)",
38622 "Antigua and Barbuda",
38632 "Armenia (Հայաստան)",
38648 "Austria (Österreich)",
38653 "Azerbaijan (Azərbaycan)",
38663 "Bahrain (البحرين)",
38668 "Bangladesh (বাংলাদেশ)",
38678 "Belarus (Беларусь)",
38683 "Belgium (België)",
38713 "Bosnia and Herzegovina (Босна и Херцеговина)",
38728 "British Indian Ocean Territory",
38733 "British Virgin Islands",
38743 "Bulgaria (България)",
38753 "Burundi (Uburundi)",
38758 "Cambodia (កម្ពុជា)",
38763 "Cameroon (Cameroun)",
38772 ["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"]
38775 "Cape Verde (Kabu Verdi)",
38780 "Caribbean Netherlands",
38791 "Central African Republic (République centrafricaine)",
38811 "Christmas Island",
38817 "Cocos (Keeling) Islands",
38828 "Comoros (جزر القمر)",
38833 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38838 "Congo (Republic) (Congo-Brazzaville)",
38858 "Croatia (Hrvatska)",
38879 "Czech Republic (Česká republika)",
38884 "Denmark (Danmark)",
38899 "Dominican Republic (República Dominicana)",
38903 ["809", "829", "849"]
38921 "Equatorial Guinea (Guinea Ecuatorial)",
38941 "Falkland Islands (Islas Malvinas)",
38946 "Faroe Islands (Føroyar)",
38967 "French Guiana (Guyane française)",
38972 "French Polynesia (Polynésie française)",
38987 "Georgia (საქართველო)",
38992 "Germany (Deutschland)",
39012 "Greenland (Kalaallit Nunaat)",
39049 "Guinea-Bissau (Guiné Bissau)",
39074 "Hungary (Magyarország)",
39079 "Iceland (Ísland)",
39099 "Iraq (العراق)",
39115 "Israel (ישראל)",
39142 "Jordan (الأردن)",
39147 "Kazakhstan (Казахстан)",
39168 "Kuwait (الكويت)",
39173 "Kyrgyzstan (Кыргызстан)",
39183 "Latvia (Latvija)",
39188 "Lebanon (لبنان)",
39203 "Libya (ليبيا)",
39213 "Lithuania (Lietuva)",
39228 "Macedonia (FYROM) (Македонија)",
39233 "Madagascar (Madagasikara)",
39263 "Marshall Islands",
39273 "Mauritania (موريتانيا)",
39278 "Mauritius (Moris)",
39299 "Moldova (Republica Moldova)",
39309 "Mongolia (Монгол)",
39314 "Montenegro (Crna Gora)",
39324 "Morocco (المغرب)",
39330 "Mozambique (Moçambique)",
39335 "Myanmar (Burma) (မြန်မာ)",
39340 "Namibia (Namibië)",
39355 "Netherlands (Nederland)",
39360 "New Caledonia (Nouvelle-Calédonie)",
39395 "North Korea (조선 민주주의 인민 공화국)",
39400 "Northern Mariana Islands",
39416 "Pakistan (پاکستان)",
39426 "Palestine (فلسطين)",
39436 "Papua New Guinea",
39478 "Réunion (La Réunion)",
39484 "Romania (România)",
39500 "Saint Barthélemy",
39511 "Saint Kitts and Nevis",
39521 "Saint Martin (Saint-Martin (partie française))",
39527 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39532 "Saint Vincent and the Grenadines",
39547 "São Tomé and Príncipe (São Tomé e Príncipe)",
39552 "Saudi Arabia (المملكة العربية السعودية)",
39557 "Senegal (Sénégal)",
39587 "Slovakia (Slovensko)",
39592 "Slovenia (Slovenija)",
39602 "Somalia (Soomaaliya)",
39612 "South Korea (대한민국)",
39617 "South Sudan (جنوب السودان)",
39627 "Sri Lanka (ශ්රී ලංකාව)",
39632 "Sudan (السودان)",
39642 "Svalbard and Jan Mayen",
39653 "Sweden (Sverige)",
39658 "Switzerland (Schweiz)",
39663 "Syria (سوريا)",
39708 "Trinidad and Tobago",
39713 "Tunisia (تونس)",
39718 "Turkey (Türkiye)",
39728 "Turks and Caicos Islands",
39738 "U.S. Virgin Islands",
39748 "Ukraine (Україна)",
39753 "United Arab Emirates (الإمارات العربية المتحدة)",
39775 "Uzbekistan (Oʻzbekiston)",
39785 "Vatican City (Città del Vaticano)",
39796 "Vietnam (Việt Nam)",
39801 "Wallis and Futuna (Wallis-et-Futuna)",
39806 "Western Sahara (الصحراء الغربية)",
39812 "Yemen (اليمن)",
39836 * This script refer to:
39837 * Title: International Telephone Input
39838 * Author: Jack O'Connor
39839 * Code version: v12.1.12
39840 * Availability: https://github.com/jackocnr/intl-tel-input.git
39844 * @class Roo.bootstrap.PhoneInput
39845 * @extends Roo.bootstrap.TriggerField
39846 * An input with International dial-code selection
39848 * @cfg {String} defaultDialCode default '+852'
39849 * @cfg {Array} preferedCountries default []
39852 * Create a new PhoneInput.
39853 * @param {Object} config Configuration options
39856 Roo.bootstrap.PhoneInput = function(config) {
39857 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39860 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39862 listWidth: undefined,
39864 selectedClass: 'active',
39866 invalidClass : "has-warning",
39868 validClass: 'has-success',
39870 allowed: '0123456789',
39873 * @cfg {String} defaultDialCode The default dial code when initializing the input
39875 defaultDialCode: '+852',
39878 * @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
39880 preferedCountries: false,
39882 getAutoCreate : function()
39884 var data = Roo.bootstrap.PhoneInputData();
39885 var align = this.labelAlign || this.parentLabelAlign();
39888 this.allCountries = [];
39889 this.dialCodeMapping = [];
39891 for (var i = 0; i < data.length; i++) {
39893 this.allCountries[i] = {
39897 priority: c[3] || 0,
39898 areaCodes: c[4] || null
39900 this.dialCodeMapping[c[2]] = {
39903 priority: c[3] || 0,
39904 areaCodes: c[4] || null
39916 cls : 'form-control tel-input',
39917 autocomplete: 'new-password'
39920 var hiddenInput = {
39923 cls: 'hidden-tel-input'
39927 hiddenInput.name = this.name;
39930 if (this.disabled) {
39931 input.disabled = true;
39934 var flag_container = {
39951 cls: this.hasFeedback ? 'has-feedback' : '',
39957 cls: 'dial-code-holder',
39964 cls: 'roo-select2-container input-group',
39971 if (this.fieldLabel.length) {
39974 tooltip: 'This field is required'
39980 cls: 'control-label',
39986 html: this.fieldLabel
39989 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39995 if(this.indicatorpos == 'right') {
39996 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40003 if(align == 'left') {
40011 if(this.labelWidth > 12){
40012 label.style = "width: " + this.labelWidth + 'px';
40014 if(this.labelWidth < 13 && this.labelmd == 0){
40015 this.labelmd = this.labelWidth;
40017 if(this.labellg > 0){
40018 label.cls += ' col-lg-' + this.labellg;
40019 input.cls += ' col-lg-' + (12 - this.labellg);
40021 if(this.labelmd > 0){
40022 label.cls += ' col-md-' + this.labelmd;
40023 container.cls += ' col-md-' + (12 - this.labelmd);
40025 if(this.labelsm > 0){
40026 label.cls += ' col-sm-' + this.labelsm;
40027 container.cls += ' col-sm-' + (12 - this.labelsm);
40029 if(this.labelxs > 0){
40030 label.cls += ' col-xs-' + this.labelxs;
40031 container.cls += ' col-xs-' + (12 - this.labelxs);
40041 var settings = this;
40043 ['xs','sm','md','lg'].map(function(size){
40044 if (settings[size]) {
40045 cfg.cls += ' col-' + size + '-' + settings[size];
40049 this.store = new Roo.data.Store({
40050 proxy : new Roo.data.MemoryProxy({}),
40051 reader : new Roo.data.JsonReader({
40062 'name' : 'dialCode',
40066 'name' : 'priority',
40070 'name' : 'areaCodes',
40077 if(!this.preferedCountries) {
40078 this.preferedCountries = [
40085 var p = this.preferedCountries.reverse();
40088 for (var i = 0; i < p.length; i++) {
40089 for (var j = 0; j < this.allCountries.length; j++) {
40090 if(this.allCountries[j].iso2 == p[i]) {
40091 var t = this.allCountries[j];
40092 this.allCountries.splice(j,1);
40093 this.allCountries.unshift(t);
40099 this.store.proxy.data = {
40101 data: this.allCountries
40107 initEvents : function()
40110 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40112 this.indicator = this.indicatorEl();
40113 this.flag = this.flagEl();
40114 this.dialCodeHolder = this.dialCodeHolderEl();
40116 this.trigger = this.el.select('div.flag-box',true).first();
40117 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40122 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40123 _this.list.setWidth(lw);
40126 this.list.on('mouseover', this.onViewOver, this);
40127 this.list.on('mousemove', this.onViewMove, this);
40128 this.inputEl().on("keyup", this.onKeyUp, this);
40130 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40132 this.view = new Roo.View(this.list, this.tpl, {
40133 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40136 this.view.on('click', this.onViewClick, this);
40137 this.setValue(this.defaultDialCode);
40140 onTriggerClick : function(e)
40142 Roo.log('trigger click');
40147 if(this.isExpanded()){
40149 this.hasFocus = false;
40151 this.store.load({});
40152 this.hasFocus = true;
40157 isExpanded : function()
40159 return this.list.isVisible();
40162 collapse : function()
40164 if(!this.isExpanded()){
40168 Roo.get(document).un('mousedown', this.collapseIf, this);
40169 Roo.get(document).un('mousewheel', this.collapseIf, this);
40170 this.fireEvent('collapse', this);
40174 expand : function()
40178 if(this.isExpanded() || !this.hasFocus){
40182 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40183 this.list.setWidth(lw);
40186 this.restrictHeight();
40188 Roo.get(document).on('mousedown', this.collapseIf, this);
40189 Roo.get(document).on('mousewheel', this.collapseIf, this);
40191 this.fireEvent('expand', this);
40194 restrictHeight : function()
40196 this.list.alignTo(this.inputEl(), this.listAlign);
40197 this.list.alignTo(this.inputEl(), this.listAlign);
40200 onViewOver : function(e, t)
40202 if(this.inKeyMode){
40205 var item = this.view.findItemFromChild(t);
40208 var index = this.view.indexOf(item);
40209 this.select(index, false);
40214 onViewClick : function(view, doFocus, el, e)
40216 var index = this.view.getSelectedIndexes()[0];
40218 var r = this.store.getAt(index);
40221 this.onSelect(r, index);
40223 if(doFocus !== false && !this.blockFocus){
40224 this.inputEl().focus();
40228 onViewMove : function(e, t)
40230 this.inKeyMode = false;
40233 select : function(index, scrollIntoView)
40235 this.selectedIndex = index;
40236 this.view.select(index);
40237 if(scrollIntoView !== false){
40238 var el = this.view.getNode(index);
40240 this.list.scrollChildIntoView(el, false);
40245 createList : function()
40247 this.list = Roo.get(document.body).createChild({
40249 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40250 style: 'display:none'
40253 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40256 collapseIf : function(e)
40258 var in_combo = e.within(this.el);
40259 var in_list = e.within(this.list);
40260 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40262 if (in_combo || in_list || is_list) {
40268 onSelect : function(record, index)
40270 if(this.fireEvent('beforeselect', this, record, index) !== false){
40272 this.setFlagClass(record.data.iso2);
40273 this.setDialCode(record.data.dialCode);
40274 this.hasFocus = false;
40276 this.fireEvent('select', this, record, index);
40280 flagEl : function()
40282 var flag = this.el.select('div.flag',true).first();
40289 dialCodeHolderEl : function()
40291 var d = this.el.select('input.dial-code-holder',true).first();
40298 setDialCode : function(v)
40300 this.dialCodeHolder.dom.value = '+'+v;
40303 setFlagClass : function(n)
40305 this.flag.dom.className = 'flag '+n;
40308 getValue : function()
40310 var v = this.inputEl().getValue();
40311 if(this.dialCodeHolder) {
40312 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40317 setValue : function(v)
40319 var d = this.getDialCode(v);
40321 //invalid dial code
40322 if(v.length == 0 || !d || d.length == 0) {
40324 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40325 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40331 this.setFlagClass(this.dialCodeMapping[d].iso2);
40332 this.setDialCode(d);
40333 this.inputEl().dom.value = v.replace('+'+d,'');
40334 this.hiddenEl().dom.value = this.getValue();
40339 getDialCode : function(v)
40343 if (v.length == 0) {
40344 return this.dialCodeHolder.dom.value;
40348 if (v.charAt(0) != "+") {
40351 var numericChars = "";
40352 for (var i = 1; i < v.length; i++) {
40353 var c = v.charAt(i);
40356 if (this.dialCodeMapping[numericChars]) {
40357 dialCode = v.substr(1, i);
40359 if (numericChars.length == 4) {
40369 this.setValue(this.defaultDialCode);
40373 hiddenEl : function()
40375 return this.el.select('input.hidden-tel-input',true).first();
40378 onKeyUp : function(e){
40380 var k = e.getKey();
40381 var c = e.getCharCode();
40384 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40385 this.allowed.indexOf(String.fromCharCode(c)) === -1
40390 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40393 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40397 this.setValue(this.getValue());
40402 * @class Roo.bootstrap.MoneyField
40403 * @extends Roo.bootstrap.ComboBox
40404 * Bootstrap MoneyField class
40407 * Create a new MoneyField.
40408 * @param {Object} config Configuration options
40411 Roo.bootstrap.MoneyField = function(config) {
40413 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40417 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40420 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40422 allowDecimals : true,
40424 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40426 decimalSeparator : ".",
40428 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40430 decimalPrecision : 0,
40432 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40434 allowNegative : true,
40436 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40440 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40442 minValue : Number.NEGATIVE_INFINITY,
40444 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40446 maxValue : Number.MAX_VALUE,
40448 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40450 minText : "The minimum value for this field is {0}",
40452 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40454 maxText : "The maximum value for this field is {0}",
40456 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40457 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40459 nanText : "{0} is not a valid number",
40461 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40465 * @cfg {String} defaults currency of the MoneyField
40466 * value should be in lkey
40468 defaultCurrency : false,
40470 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40472 thousandsDelimiter : false,
40482 getAutoCreate : function()
40484 var align = this.labelAlign || this.parentLabelAlign();
40496 cls : 'form-control roo-money-amount-input',
40497 autocomplete: 'new-password'
40500 var hiddenInput = {
40504 cls: 'hidden-number-input'
40508 hiddenInput.name = this.name;
40511 if (this.disabled) {
40512 input.disabled = true;
40515 var clg = 12 - this.inputlg;
40516 var cmd = 12 - this.inputmd;
40517 var csm = 12 - this.inputsm;
40518 var cxs = 12 - this.inputxs;
40522 cls : 'row roo-money-field',
40526 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40530 cls: 'roo-select2-container input-group',
40534 cls : 'form-control roo-money-currency-input',
40535 autocomplete: 'new-password',
40537 name : this.currencyName
40541 cls : 'input-group-addon',
40555 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40559 cls: this.hasFeedback ? 'has-feedback' : '',
40570 if (this.fieldLabel.length) {
40573 tooltip: 'This field is required'
40579 cls: 'control-label',
40585 html: this.fieldLabel
40588 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40594 if(this.indicatorpos == 'right') {
40595 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40602 if(align == 'left') {
40610 if(this.labelWidth > 12){
40611 label.style = "width: " + this.labelWidth + 'px';
40613 if(this.labelWidth < 13 && this.labelmd == 0){
40614 this.labelmd = this.labelWidth;
40616 if(this.labellg > 0){
40617 label.cls += ' col-lg-' + this.labellg;
40618 input.cls += ' col-lg-' + (12 - this.labellg);
40620 if(this.labelmd > 0){
40621 label.cls += ' col-md-' + this.labelmd;
40622 container.cls += ' col-md-' + (12 - this.labelmd);
40624 if(this.labelsm > 0){
40625 label.cls += ' col-sm-' + this.labelsm;
40626 container.cls += ' col-sm-' + (12 - this.labelsm);
40628 if(this.labelxs > 0){
40629 label.cls += ' col-xs-' + this.labelxs;
40630 container.cls += ' col-xs-' + (12 - this.labelxs);
40641 var settings = this;
40643 ['xs','sm','md','lg'].map(function(size){
40644 if (settings[size]) {
40645 cfg.cls += ' col-' + size + '-' + settings[size];
40652 initEvents : function()
40654 this.indicator = this.indicatorEl();
40656 this.initCurrencyEvent();
40658 this.initNumberEvent();
40661 initCurrencyEvent : function()
40664 throw "can not find store for combo";
40667 this.store = Roo.factory(this.store, Roo.data);
40668 this.store.parent = this;
40672 this.triggerEl = this.el.select('.input-group-addon', true).first();
40674 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40679 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40680 _this.list.setWidth(lw);
40683 this.list.on('mouseover', this.onViewOver, this);
40684 this.list.on('mousemove', this.onViewMove, this);
40685 this.list.on('scroll', this.onViewScroll, this);
40688 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40691 this.view = new Roo.View(this.list, this.tpl, {
40692 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40695 this.view.on('click', this.onViewClick, this);
40697 this.store.on('beforeload', this.onBeforeLoad, this);
40698 this.store.on('load', this.onLoad, this);
40699 this.store.on('loadexception', this.onLoadException, this);
40701 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40702 "up" : function(e){
40703 this.inKeyMode = true;
40707 "down" : function(e){
40708 if(!this.isExpanded()){
40709 this.onTriggerClick();
40711 this.inKeyMode = true;
40716 "enter" : function(e){
40719 if(this.fireEvent("specialkey", this, e)){
40720 this.onViewClick(false);
40726 "esc" : function(e){
40730 "tab" : function(e){
40733 if(this.fireEvent("specialkey", this, e)){
40734 this.onViewClick(false);
40742 doRelay : function(foo, bar, hname){
40743 if(hname == 'down' || this.scope.isExpanded()){
40744 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40752 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40756 initNumberEvent : function(e)
40758 this.inputEl().on("keydown" , this.fireKey, this);
40759 this.inputEl().on("focus", this.onFocus, this);
40760 this.inputEl().on("blur", this.onBlur, this);
40762 this.inputEl().relayEvent('keyup', this);
40764 if(this.indicator){
40765 this.indicator.addClass('invisible');
40768 this.originalValue = this.getValue();
40770 if(this.validationEvent == 'keyup'){
40771 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40772 this.inputEl().on('keyup', this.filterValidation, this);
40774 else if(this.validationEvent !== false){
40775 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40778 if(this.selectOnFocus){
40779 this.on("focus", this.preFocus, this);
40782 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40783 this.inputEl().on("keypress", this.filterKeys, this);
40785 this.inputEl().relayEvent('keypress', this);
40788 var allowed = "0123456789";
40790 if(this.allowDecimals){
40791 allowed += this.decimalSeparator;
40794 if(this.allowNegative){
40798 if(this.thousandsDelimiter) {
40802 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40804 var keyPress = function(e){
40806 var k = e.getKey();
40808 var c = e.getCharCode();
40811 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40812 allowed.indexOf(String.fromCharCode(c)) === -1
40818 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40822 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40827 this.inputEl().on("keypress", keyPress, this);
40831 onTriggerClick : function(e)
40838 this.loadNext = false;
40840 if(this.isExpanded()){
40845 this.hasFocus = true;
40847 if(this.triggerAction == 'all') {
40848 this.doQuery(this.allQuery, true);
40852 this.doQuery(this.getRawValue());
40855 getCurrency : function()
40857 var v = this.currencyEl().getValue();
40862 restrictHeight : function()
40864 this.list.alignTo(this.currencyEl(), this.listAlign);
40865 this.list.alignTo(this.currencyEl(), this.listAlign);
40868 onViewClick : function(view, doFocus, el, e)
40870 var index = this.view.getSelectedIndexes()[0];
40872 var r = this.store.getAt(index);
40875 this.onSelect(r, index);
40879 onSelect : function(record, index){
40881 if(this.fireEvent('beforeselect', this, record, index) !== false){
40883 this.setFromCurrencyData(index > -1 ? record.data : false);
40887 this.fireEvent('select', this, record, index);
40891 setFromCurrencyData : function(o)
40895 this.lastCurrency = o;
40897 if (this.currencyField) {
40898 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40900 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40903 this.lastSelectionText = currency;
40905 //setting default currency
40906 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40907 this.setCurrency(this.defaultCurrency);
40911 this.setCurrency(currency);
40914 setFromData : function(o)
40918 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40920 this.setFromCurrencyData(c);
40925 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40927 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40930 this.setValue(value);
40934 setCurrency : function(v)
40936 this.currencyValue = v;
40939 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40944 setValue : function(v)
40946 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40952 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40954 this.inputEl().dom.value = (v == '') ? '' :
40955 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40957 if(!this.allowZero && v === '0') {
40958 this.hiddenEl().dom.value = '';
40959 this.inputEl().dom.value = '';
40966 getRawValue : function()
40968 var v = this.inputEl().getValue();
40973 getValue : function()
40975 return this.fixPrecision(this.parseValue(this.getRawValue()));
40978 parseValue : function(value)
40980 if(this.thousandsDelimiter) {
40982 r = new RegExp(",", "g");
40983 value = value.replace(r, "");
40986 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40987 return isNaN(value) ? '' : value;
40991 fixPrecision : function(value)
40993 if(this.thousandsDelimiter) {
40995 r = new RegExp(",", "g");
40996 value = value.replace(r, "");
40999 var nan = isNaN(value);
41001 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41002 return nan ? '' : value;
41004 return parseFloat(value).toFixed(this.decimalPrecision);
41007 decimalPrecisionFcn : function(v)
41009 return Math.floor(v);
41012 validateValue : function(value)
41014 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41018 var num = this.parseValue(value);
41021 this.markInvalid(String.format(this.nanText, value));
41025 if(num < this.minValue){
41026 this.markInvalid(String.format(this.minText, this.minValue));
41030 if(num > this.maxValue){
41031 this.markInvalid(String.format(this.maxText, this.maxValue));
41038 validate : function()
41040 if(this.disabled || this.allowBlank){
41045 var currency = this.getCurrency();
41047 if(this.validateValue(this.getRawValue()) && currency.length){
41052 this.markInvalid();
41056 getName: function()
41061 beforeBlur : function()
41067 var v = this.parseValue(this.getRawValue());
41074 onBlur : function()
41078 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41079 //this.el.removeClass(this.focusClass);
41082 this.hasFocus = false;
41084 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41088 var v = this.getValue();
41090 if(String(v) !== String(this.startValue)){
41091 this.fireEvent('change', this, v, this.startValue);
41094 this.fireEvent("blur", this);
41097 inputEl : function()
41099 return this.el.select('.roo-money-amount-input', true).first();
41102 currencyEl : function()
41104 return this.el.select('.roo-money-currency-input', true).first();
41107 hiddenEl : function()
41109 return this.el.select('input.hidden-number-input',true).first();