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; j < w.length; j++) {
7124 var size_cls = w[j].split("-");
7126 if(!Number.isInteger(size_cls[1] * 1)) {
7130 if(!this.colModel.config[col_index][size_cls[0]]) {
7134 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7138 h_row[0].classList.replace(
7139 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7140 "col-"+size_cls[0]+"-"+size_cls[1]
7143 for(var i = 0; i < rows.length; i++) {
7145 var size_cls = w[j].split("-");
7147 if(!Number.isInteger(size_cls[1] * 1)) {
7151 if(!this.colModel.config[col_index][size_cls[0]]) {
7155 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7159 rows[i].classList.replace(
7160 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7161 "col-"+size_cls[0]+"-"+size_cls[1]
7165 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7180 * @class Roo.bootstrap.TableCell
7181 * @extends Roo.bootstrap.Component
7182 * Bootstrap TableCell class
7183 * @cfg {String} html cell contain text
7184 * @cfg {String} cls cell class
7185 * @cfg {String} tag cell tag (td|th) default td
7186 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7187 * @cfg {String} align Aligns the content in a cell
7188 * @cfg {String} axis Categorizes cells
7189 * @cfg {String} bgcolor Specifies the background color of a cell
7190 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7191 * @cfg {Number} colspan Specifies the number of columns a cell should span
7192 * @cfg {String} headers Specifies one or more header cells a cell is related to
7193 * @cfg {Number} height Sets the height of a cell
7194 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7195 * @cfg {Number} rowspan Sets the number of rows a cell should span
7196 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7197 * @cfg {String} valign Vertical aligns the content in a cell
7198 * @cfg {Number} width Specifies the width of a cell
7201 * Create a new TableCell
7202 * @param {Object} config The config object
7205 Roo.bootstrap.TableCell = function(config){
7206 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7209 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7229 getAutoCreate : function(){
7230 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7250 cfg.align=this.align
7256 cfg.bgcolor=this.bgcolor
7259 cfg.charoff=this.charoff
7262 cfg.colspan=this.colspan
7265 cfg.headers=this.headers
7268 cfg.height=this.height
7271 cfg.nowrap=this.nowrap
7274 cfg.rowspan=this.rowspan
7277 cfg.scope=this.scope
7280 cfg.valign=this.valign
7283 cfg.width=this.width
7302 * @class Roo.bootstrap.TableRow
7303 * @extends Roo.bootstrap.Component
7304 * Bootstrap TableRow class
7305 * @cfg {String} cls row class
7306 * @cfg {String} align Aligns the content in a table row
7307 * @cfg {String} bgcolor Specifies a background color for a table row
7308 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7309 * @cfg {String} valign Vertical aligns the content in a table row
7312 * Create a new TableRow
7313 * @param {Object} config The config object
7316 Roo.bootstrap.TableRow = function(config){
7317 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7320 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7328 getAutoCreate : function(){
7329 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7339 cfg.align = this.align;
7342 cfg.bgcolor = this.bgcolor;
7345 cfg.charoff = this.charoff;
7348 cfg.valign = this.valign;
7366 * @class Roo.bootstrap.TableBody
7367 * @extends Roo.bootstrap.Component
7368 * Bootstrap TableBody class
7369 * @cfg {String} cls element class
7370 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7371 * @cfg {String} align Aligns the content inside the element
7372 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7373 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7376 * Create a new TableBody
7377 * @param {Object} config The config object
7380 Roo.bootstrap.TableBody = function(config){
7381 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7384 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7392 getAutoCreate : function(){
7393 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7407 cfg.align = this.align;
7410 cfg.charoff = this.charoff;
7413 cfg.valign = this.valign;
7420 // initEvents : function()
7427 // this.store = Roo.factory(this.store, Roo.data);
7428 // this.store.on('load', this.onLoad, this);
7430 // this.store.load();
7434 // onLoad: function ()
7436 // this.fireEvent('load', this);
7446 * Ext JS Library 1.1.1
7447 * Copyright(c) 2006-2007, Ext JS, LLC.
7449 * Originally Released Under LGPL - original licence link has changed is not relivant.
7452 * <script type="text/javascript">
7455 // as we use this in bootstrap.
7456 Roo.namespace('Roo.form');
7458 * @class Roo.form.Action
7459 * Internal Class used to handle form actions
7461 * @param {Roo.form.BasicForm} el The form element or its id
7462 * @param {Object} config Configuration options
7467 // define the action interface
7468 Roo.form.Action = function(form, options){
7470 this.options = options || {};
7473 * Client Validation Failed
7476 Roo.form.Action.CLIENT_INVALID = 'client';
7478 * Server Validation Failed
7481 Roo.form.Action.SERVER_INVALID = 'server';
7483 * Connect to Server Failed
7486 Roo.form.Action.CONNECT_FAILURE = 'connect';
7488 * Reading Data from Server Failed
7491 Roo.form.Action.LOAD_FAILURE = 'load';
7493 Roo.form.Action.prototype = {
7495 failureType : undefined,
7496 response : undefined,
7500 run : function(options){
7505 success : function(response){
7510 handleResponse : function(response){
7514 // default connection failure
7515 failure : function(response){
7517 this.response = response;
7518 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7519 this.form.afterAction(this, false);
7522 processResponse : function(response){
7523 this.response = response;
7524 if(!response.responseText){
7527 this.result = this.handleResponse(response);
7531 // utility functions used internally
7532 getUrl : function(appendParams){
7533 var url = this.options.url || this.form.url || this.form.el.dom.action;
7535 var p = this.getParams();
7537 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7543 getMethod : function(){
7544 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7547 getParams : function(){
7548 var bp = this.form.baseParams;
7549 var p = this.options.params;
7551 if(typeof p == "object"){
7552 p = Roo.urlEncode(Roo.applyIf(p, bp));
7553 }else if(typeof p == 'string' && bp){
7554 p += '&' + Roo.urlEncode(bp);
7557 p = Roo.urlEncode(bp);
7562 createCallback : function(){
7564 success: this.success,
7565 failure: this.failure,
7567 timeout: (this.form.timeout*1000),
7568 upload: this.form.fileUpload ? this.success : undefined
7573 Roo.form.Action.Submit = function(form, options){
7574 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7577 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7580 haveProgress : false,
7581 uploadComplete : false,
7583 // uploadProgress indicator.
7584 uploadProgress : function()
7586 if (!this.form.progressUrl) {
7590 if (!this.haveProgress) {
7591 Roo.MessageBox.progress("Uploading", "Uploading");
7593 if (this.uploadComplete) {
7594 Roo.MessageBox.hide();
7598 this.haveProgress = true;
7600 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7602 var c = new Roo.data.Connection();
7604 url : this.form.progressUrl,
7609 success : function(req){
7610 //console.log(data);
7614 rdata = Roo.decode(req.responseText)
7616 Roo.log("Invalid data from server..");
7620 if (!rdata || !rdata.success) {
7622 Roo.MessageBox.alert(Roo.encode(rdata));
7625 var data = rdata.data;
7627 if (this.uploadComplete) {
7628 Roo.MessageBox.hide();
7633 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7634 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7637 this.uploadProgress.defer(2000,this);
7640 failure: function(data) {
7641 Roo.log('progress url failed ');
7652 // run get Values on the form, so it syncs any secondary forms.
7653 this.form.getValues();
7655 var o = this.options;
7656 var method = this.getMethod();
7657 var isPost = method == 'POST';
7658 if(o.clientValidation === false || this.form.isValid()){
7660 if (this.form.progressUrl) {
7661 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7662 (new Date() * 1) + '' + Math.random());
7667 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7668 form:this.form.el.dom,
7669 url:this.getUrl(!isPost),
7671 params:isPost ? this.getParams() : null,
7672 isUpload: this.form.fileUpload
7675 this.uploadProgress();
7677 }else if (o.clientValidation !== false){ // client validation failed
7678 this.failureType = Roo.form.Action.CLIENT_INVALID;
7679 this.form.afterAction(this, false);
7683 success : function(response)
7685 this.uploadComplete= true;
7686 if (this.haveProgress) {
7687 Roo.MessageBox.hide();
7691 var result = this.processResponse(response);
7692 if(result === true || result.success){
7693 this.form.afterAction(this, true);
7697 this.form.markInvalid(result.errors);
7698 this.failureType = Roo.form.Action.SERVER_INVALID;
7700 this.form.afterAction(this, false);
7702 failure : function(response)
7704 this.uploadComplete= true;
7705 if (this.haveProgress) {
7706 Roo.MessageBox.hide();
7709 this.response = response;
7710 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7711 this.form.afterAction(this, false);
7714 handleResponse : function(response){
7715 if(this.form.errorReader){
7716 var rs = this.form.errorReader.read(response);
7719 for(var i = 0, len = rs.records.length; i < len; i++) {
7720 var r = rs.records[i];
7724 if(errors.length < 1){
7728 success : rs.success,
7734 ret = Roo.decode(response.responseText);
7738 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7748 Roo.form.Action.Load = function(form, options){
7749 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7750 this.reader = this.form.reader;
7753 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7758 Roo.Ajax.request(Roo.apply(
7759 this.createCallback(), {
7760 method:this.getMethod(),
7761 url:this.getUrl(false),
7762 params:this.getParams()
7766 success : function(response){
7768 var result = this.processResponse(response);
7769 if(result === true || !result.success || !result.data){
7770 this.failureType = Roo.form.Action.LOAD_FAILURE;
7771 this.form.afterAction(this, false);
7774 this.form.clearInvalid();
7775 this.form.setValues(result.data);
7776 this.form.afterAction(this, true);
7779 handleResponse : function(response){
7780 if(this.form.reader){
7781 var rs = this.form.reader.read(response);
7782 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7784 success : rs.success,
7788 return Roo.decode(response.responseText);
7792 Roo.form.Action.ACTION_TYPES = {
7793 'load' : Roo.form.Action.Load,
7794 'submit' : Roo.form.Action.Submit
7803 * @class Roo.bootstrap.Form
7804 * @extends Roo.bootstrap.Component
7805 * Bootstrap Form class
7806 * @cfg {String} method GET | POST (default POST)
7807 * @cfg {String} labelAlign top | left (default top)
7808 * @cfg {String} align left | right - for navbars
7809 * @cfg {Boolean} loadMask load mask when submit (default true)
7814 * @param {Object} config The config object
7818 Roo.bootstrap.Form = function(config){
7820 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7822 Roo.bootstrap.Form.popover.apply();
7826 * @event clientvalidation
7827 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7828 * @param {Form} this
7829 * @param {Boolean} valid true if the form has passed client-side validation
7831 clientvalidation: true,
7833 * @event beforeaction
7834 * Fires before any action is performed. Return false to cancel the action.
7835 * @param {Form} this
7836 * @param {Action} action The action to be performed
7840 * @event actionfailed
7841 * Fires when an action fails.
7842 * @param {Form} this
7843 * @param {Action} action The action that failed
7845 actionfailed : true,
7847 * @event actioncomplete
7848 * Fires when an action is completed.
7849 * @param {Form} this
7850 * @param {Action} action The action that completed
7852 actioncomplete : true
7856 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7859 * @cfg {String} method
7860 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7865 * The URL to use for form actions if one isn't supplied in the action options.
7868 * @cfg {Boolean} fileUpload
7869 * Set to true if this form is a file upload.
7873 * @cfg {Object} baseParams
7874 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7878 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7882 * @cfg {Sting} align (left|right) for navbar forms
7887 activeAction : null,
7890 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7891 * element by passing it or its id or mask the form itself by passing in true.
7894 waitMsgTarget : false,
7899 * @cfg {Boolean} errorMask (true|false) default false
7904 * @cfg {Number} maskOffset Default 100
7909 * @cfg {Boolean} maskBody
7913 getAutoCreate : function(){
7917 method : this.method || 'POST',
7918 id : this.id || Roo.id(),
7921 if (this.parent().xtype.match(/^Nav/)) {
7922 cfg.cls = 'navbar-form navbar-' + this.align;
7926 if (this.labelAlign == 'left' ) {
7927 cfg.cls += ' form-horizontal';
7933 initEvents : function()
7935 this.el.on('submit', this.onSubmit, this);
7936 // this was added as random key presses on the form where triggering form submit.
7937 this.el.on('keypress', function(e) {
7938 if (e.getCharCode() != 13) {
7941 // we might need to allow it for textareas.. and some other items.
7942 // check e.getTarget().
7944 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7948 Roo.log("keypress blocked");
7956 onSubmit : function(e){
7961 * Returns true if client-side validation on the form is successful.
7964 isValid : function(){
7965 var items = this.getItems();
7969 items.each(function(f){
7975 Roo.log('invalid field: ' + f.name);
7979 if(!target && f.el.isVisible(true)){
7985 if(this.errorMask && !valid){
7986 Roo.bootstrap.Form.popover.mask(this, target);
7993 * Returns true if any fields in this form have changed since their original load.
7996 isDirty : function(){
7998 var items = this.getItems();
7999 items.each(function(f){
8009 * Performs a predefined action (submit or load) or custom actions you define on this form.
8010 * @param {String} actionName The name of the action type
8011 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8012 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8013 * accept other config options):
8015 Property Type Description
8016 ---------------- --------------- ----------------------------------------------------------------------------------
8017 url String The url for the action (defaults to the form's url)
8018 method String The form method to use (defaults to the form's method, or POST if not defined)
8019 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8020 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8021 validate the form on the client (defaults to false)
8023 * @return {BasicForm} this
8025 doAction : function(action, options){
8026 if(typeof action == 'string'){
8027 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8029 if(this.fireEvent('beforeaction', this, action) !== false){
8030 this.beforeAction(action);
8031 action.run.defer(100, action);
8037 beforeAction : function(action){
8038 var o = action.options;
8043 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8045 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8048 // not really supported yet.. ??
8050 //if(this.waitMsgTarget === true){
8051 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8052 //}else if(this.waitMsgTarget){
8053 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8054 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8056 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8062 afterAction : function(action, success){
8063 this.activeAction = null;
8064 var o = action.options;
8069 Roo.get(document.body).unmask();
8075 //if(this.waitMsgTarget === true){
8076 // this.el.unmask();
8077 //}else if(this.waitMsgTarget){
8078 // this.waitMsgTarget.unmask();
8080 // Roo.MessageBox.updateProgress(1);
8081 // Roo.MessageBox.hide();
8088 Roo.callback(o.success, o.scope, [this, action]);
8089 this.fireEvent('actioncomplete', this, action);
8093 // failure condition..
8094 // we have a scenario where updates need confirming.
8095 // eg. if a locking scenario exists..
8096 // we look for { errors : { needs_confirm : true }} in the response.
8098 (typeof(action.result) != 'undefined') &&
8099 (typeof(action.result.errors) != 'undefined') &&
8100 (typeof(action.result.errors.needs_confirm) != 'undefined')
8103 Roo.log("not supported yet");
8106 Roo.MessageBox.confirm(
8107 "Change requires confirmation",
8108 action.result.errorMsg,
8113 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8123 Roo.callback(o.failure, o.scope, [this, action]);
8124 // show an error message if no failed handler is set..
8125 if (!this.hasListener('actionfailed')) {
8126 Roo.log("need to add dialog support");
8128 Roo.MessageBox.alert("Error",
8129 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8130 action.result.errorMsg :
8131 "Saving Failed, please check your entries or try again"
8136 this.fireEvent('actionfailed', this, action);
8141 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8142 * @param {String} id The value to search for
8145 findField : function(id){
8146 var items = this.getItems();
8147 var field = items.get(id);
8149 items.each(function(f){
8150 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8157 return field || null;
8160 * Mark fields in this form invalid in bulk.
8161 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8162 * @return {BasicForm} this
8164 markInvalid : function(errors){
8165 if(errors instanceof Array){
8166 for(var i = 0, len = errors.length; i < len; i++){
8167 var fieldError = errors[i];
8168 var f = this.findField(fieldError.id);
8170 f.markInvalid(fieldError.msg);
8176 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8177 field.markInvalid(errors[id]);
8181 //Roo.each(this.childForms || [], function (f) {
8182 // f.markInvalid(errors);
8189 * Set values for fields in this form in bulk.
8190 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8191 * @return {BasicForm} this
8193 setValues : function(values){
8194 if(values instanceof Array){ // array of objects
8195 for(var i = 0, len = values.length; i < len; i++){
8197 var f = this.findField(v.id);
8199 f.setValue(v.value);
8200 if(this.trackResetOnLoad){
8201 f.originalValue = f.getValue();
8205 }else{ // object hash
8208 if(typeof values[id] != 'function' && (field = this.findField(id))){
8210 if (field.setFromData &&
8212 field.displayField &&
8213 // combos' with local stores can
8214 // be queried via setValue()
8215 // to set their value..
8216 (field.store && !field.store.isLocal)
8220 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8221 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8222 field.setFromData(sd);
8224 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8226 field.setFromData(values);
8229 field.setValue(values[id]);
8233 if(this.trackResetOnLoad){
8234 field.originalValue = field.getValue();
8240 //Roo.each(this.childForms || [], function (f) {
8241 // f.setValues(values);
8248 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8249 * they are returned as an array.
8250 * @param {Boolean} asString
8253 getValues : function(asString){
8254 //if (this.childForms) {
8255 // copy values from the child forms
8256 // Roo.each(this.childForms, function (f) {
8257 // this.setValues(f.getValues());
8263 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8264 if(asString === true){
8267 return Roo.urlDecode(fs);
8271 * Returns the fields in this form as an object with key/value pairs.
8272 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8275 getFieldValues : function(with_hidden)
8277 var items = this.getItems();
8279 items.each(function(f){
8285 var v = f.getValue();
8287 if (f.inputType =='radio') {
8288 if (typeof(ret[f.getName()]) == 'undefined') {
8289 ret[f.getName()] = ''; // empty..
8292 if (!f.el.dom.checked) {
8300 if(f.xtype == 'MoneyField'){
8301 ret[f.currencyName] = f.getCurrency();
8304 // not sure if this supported any more..
8305 if ((typeof(v) == 'object') && f.getRawValue) {
8306 v = f.getRawValue() ; // dates..
8308 // combo boxes where name != hiddenName...
8309 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8310 ret[f.name] = f.getRawValue();
8312 ret[f.getName()] = v;
8319 * Clears all invalid messages in this form.
8320 * @return {BasicForm} this
8322 clearInvalid : function(){
8323 var items = this.getItems();
8325 items.each(function(f){
8334 * @return {BasicForm} this
8337 var items = this.getItems();
8338 items.each(function(f){
8342 Roo.each(this.childForms || [], function (f) {
8350 getItems : function()
8352 var r=new Roo.util.MixedCollection(false, function(o){
8353 return o.id || (o.id = Roo.id());
8355 var iter = function(el) {
8362 Roo.each(el.items,function(e) {
8371 hideFields : function(items)
8373 Roo.each(items, function(i){
8375 var f = this.findField(i);
8386 showFields : function(items)
8388 Roo.each(items, function(i){
8390 var f = this.findField(i);
8403 Roo.apply(Roo.bootstrap.Form, {
8430 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8431 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8432 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8433 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8436 this.maskEl.top.enableDisplayMode("block");
8437 this.maskEl.left.enableDisplayMode("block");
8438 this.maskEl.bottom.enableDisplayMode("block");
8439 this.maskEl.right.enableDisplayMode("block");
8441 this.toolTip = new Roo.bootstrap.Tooltip({
8442 cls : 'roo-form-error-popover',
8444 'left' : ['r-l', [-2,0], 'right'],
8445 'right' : ['l-r', [2,0], 'left'],
8446 'bottom' : ['tl-bl', [0,2], 'top'],
8447 'top' : [ 'bl-tl', [0,-2], 'bottom']
8451 this.toolTip.render(Roo.get(document.body));
8453 this.toolTip.el.enableDisplayMode("block");
8455 Roo.get(document.body).on('click', function(){
8459 Roo.get(document.body).on('touchstart', function(){
8463 this.isApplied = true
8466 mask : function(form, target)
8470 this.target = target;
8472 if(!this.form.errorMask || !target.el){
8476 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8478 Roo.log(scrollable);
8480 var ot = this.target.el.calcOffsetsTo(scrollable);
8482 var scrollTo = ot[1] - this.form.maskOffset;
8484 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8486 scrollable.scrollTo('top', scrollTo);
8488 var box = this.target.el.getBox();
8490 var zIndex = Roo.bootstrap.Modal.zIndex++;
8493 this.maskEl.top.setStyle('position', 'absolute');
8494 this.maskEl.top.setStyle('z-index', zIndex);
8495 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8496 this.maskEl.top.setLeft(0);
8497 this.maskEl.top.setTop(0);
8498 this.maskEl.top.show();
8500 this.maskEl.left.setStyle('position', 'absolute');
8501 this.maskEl.left.setStyle('z-index', zIndex);
8502 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8503 this.maskEl.left.setLeft(0);
8504 this.maskEl.left.setTop(box.y - this.padding);
8505 this.maskEl.left.show();
8507 this.maskEl.bottom.setStyle('position', 'absolute');
8508 this.maskEl.bottom.setStyle('z-index', zIndex);
8509 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8510 this.maskEl.bottom.setLeft(0);
8511 this.maskEl.bottom.setTop(box.bottom + this.padding);
8512 this.maskEl.bottom.show();
8514 this.maskEl.right.setStyle('position', 'absolute');
8515 this.maskEl.right.setStyle('z-index', zIndex);
8516 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8517 this.maskEl.right.setLeft(box.right + this.padding);
8518 this.maskEl.right.setTop(box.y - this.padding);
8519 this.maskEl.right.show();
8521 this.toolTip.bindEl = this.target.el;
8523 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8525 var tip = this.target.blankText;
8527 if(this.target.getValue() !== '' ) {
8529 if (this.target.invalidText.length) {
8530 tip = this.target.invalidText;
8531 } else if (this.target.regexText.length){
8532 tip = this.target.regexText;
8536 this.toolTip.show(tip);
8538 this.intervalID = window.setInterval(function() {
8539 Roo.bootstrap.Form.popover.unmask();
8542 window.onwheel = function(){ return false;};
8544 (function(){ this.isMasked = true; }).defer(500, this);
8550 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8554 this.maskEl.top.setStyle('position', 'absolute');
8555 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8556 this.maskEl.top.hide();
8558 this.maskEl.left.setStyle('position', 'absolute');
8559 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8560 this.maskEl.left.hide();
8562 this.maskEl.bottom.setStyle('position', 'absolute');
8563 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8564 this.maskEl.bottom.hide();
8566 this.maskEl.right.setStyle('position', 'absolute');
8567 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8568 this.maskEl.right.hide();
8570 this.toolTip.hide();
8572 this.toolTip.el.hide();
8574 window.onwheel = function(){ return true;};
8576 if(this.intervalID){
8577 window.clearInterval(this.intervalID);
8578 this.intervalID = false;
8581 this.isMasked = false;
8591 * Ext JS Library 1.1.1
8592 * Copyright(c) 2006-2007, Ext JS, LLC.
8594 * Originally Released Under LGPL - original licence link has changed is not relivant.
8597 * <script type="text/javascript">
8600 * @class Roo.form.VTypes
8601 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8604 Roo.form.VTypes = function(){
8605 // closure these in so they are only created once.
8606 var alpha = /^[a-zA-Z_]+$/;
8607 var alphanum = /^[a-zA-Z0-9_]+$/;
8608 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8609 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8611 // All these messages and functions are configurable
8614 * The function used to validate email addresses
8615 * @param {String} value The email address
8617 'email' : function(v){
8618 return email.test(v);
8621 * The error text to display when the email validation function returns false
8624 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8626 * The keystroke filter mask to be applied on email input
8629 'emailMask' : /[a-z0-9_\.\-@]/i,
8632 * The function used to validate URLs
8633 * @param {String} value The URL
8635 'url' : function(v){
8639 * The error text to display when the url validation function returns false
8642 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8645 * The function used to validate alpha values
8646 * @param {String} value The value
8648 'alpha' : function(v){
8649 return alpha.test(v);
8652 * The error text to display when the alpha validation function returns false
8655 'alphaText' : 'This field should only contain letters and _',
8657 * The keystroke filter mask to be applied on alpha input
8660 'alphaMask' : /[a-z_]/i,
8663 * The function used to validate alphanumeric values
8664 * @param {String} value The value
8666 'alphanum' : function(v){
8667 return alphanum.test(v);
8670 * The error text to display when the alphanumeric validation function returns false
8673 'alphanumText' : 'This field should only contain letters, numbers and _',
8675 * The keystroke filter mask to be applied on alphanumeric input
8678 'alphanumMask' : /[a-z0-9_]/i
8688 * @class Roo.bootstrap.Input
8689 * @extends Roo.bootstrap.Component
8690 * Bootstrap Input class
8691 * @cfg {Boolean} disabled is it disabled
8692 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8693 * @cfg {String} name name of the input
8694 * @cfg {string} fieldLabel - the label associated
8695 * @cfg {string} placeholder - placeholder to put in text.
8696 * @cfg {string} before - input group add on before
8697 * @cfg {string} after - input group add on after
8698 * @cfg {string} size - (lg|sm) or leave empty..
8699 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8700 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8701 * @cfg {Number} md colspan out of 12 for computer-sized screens
8702 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8703 * @cfg {string} value default value of the input
8704 * @cfg {Number} labelWidth set the width of label
8705 * @cfg {Number} labellg set the width of label (1-12)
8706 * @cfg {Number} labelmd set the width of label (1-12)
8707 * @cfg {Number} labelsm set the width of label (1-12)
8708 * @cfg {Number} labelxs set the width of label (1-12)
8709 * @cfg {String} labelAlign (top|left)
8710 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8711 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8712 * @cfg {String} indicatorpos (left|right) default left
8713 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8714 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8716 * @cfg {String} align (left|center|right) Default left
8717 * @cfg {Boolean} forceFeedback (true|false) Default false
8720 * Create a new Input
8721 * @param {Object} config The config object
8724 Roo.bootstrap.Input = function(config){
8726 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8731 * Fires when this field receives input focus.
8732 * @param {Roo.form.Field} this
8737 * Fires when this field loses input focus.
8738 * @param {Roo.form.Field} this
8743 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8744 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8745 * @param {Roo.form.Field} this
8746 * @param {Roo.EventObject} e The event object
8751 * Fires just before the field blurs if the field value has changed.
8752 * @param {Roo.form.Field} this
8753 * @param {Mixed} newValue The new value
8754 * @param {Mixed} oldValue The original value
8759 * Fires after the field has been marked as invalid.
8760 * @param {Roo.form.Field} this
8761 * @param {String} msg The validation message
8766 * Fires after the field has been validated with no errors.
8767 * @param {Roo.form.Field} this
8772 * Fires after the key up
8773 * @param {Roo.form.Field} this
8774 * @param {Roo.EventObject} e The event Object
8780 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8782 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8783 automatic validation (defaults to "keyup").
8785 validationEvent : "keyup",
8787 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8789 validateOnBlur : true,
8791 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8793 validationDelay : 250,
8795 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8797 focusClass : "x-form-focus", // not needed???
8801 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8803 invalidClass : "has-warning",
8806 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8808 validClass : "has-success",
8811 * @cfg {Boolean} hasFeedback (true|false) default true
8816 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8818 invalidFeedbackClass : "glyphicon-warning-sign",
8821 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8823 validFeedbackClass : "glyphicon-ok",
8826 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8828 selectOnFocus : false,
8831 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8835 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8840 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8842 disableKeyFilter : false,
8845 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8849 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8853 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8855 blankText : "Please complete this mandatory field",
8858 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8862 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8864 maxLength : Number.MAX_VALUE,
8866 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8868 minLengthText : "The minimum length for this field is {0}",
8870 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8872 maxLengthText : "The maximum length for this field is {0}",
8876 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8877 * If available, this function will be called only after the basic validators all return true, and will be passed the
8878 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8882 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8883 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8884 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8888 * @cfg {String} regexText -- Depricated - use Invalid Text
8893 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8899 autocomplete: false,
8918 formatedValue : false,
8919 forceFeedback : false,
8921 indicatorpos : 'left',
8931 parentLabelAlign : function()
8934 while (parent.parent()) {
8935 parent = parent.parent();
8936 if (typeof(parent.labelAlign) !='undefined') {
8937 return parent.labelAlign;
8944 getAutoCreate : function()
8946 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8952 if(this.inputType != 'hidden'){
8953 cfg.cls = 'form-group' //input-group
8959 type : this.inputType,
8961 cls : 'form-control',
8962 placeholder : this.placeholder || '',
8963 autocomplete : this.autocomplete || 'new-password'
8966 if(this.capture.length){
8967 input.capture = this.capture;
8970 if(this.accept.length){
8971 input.accept = this.accept + "/*";
8975 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8978 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8979 input.maxLength = this.maxLength;
8982 if (this.disabled) {
8983 input.disabled=true;
8986 if (this.readOnly) {
8987 input.readonly=true;
8991 input.name = this.name;
8995 input.cls += ' input-' + this.size;
8999 ['xs','sm','md','lg'].map(function(size){
9000 if (settings[size]) {
9001 cfg.cls += ' col-' + size + '-' + settings[size];
9005 var inputblock = input;
9009 cls: 'glyphicon form-control-feedback'
9012 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9015 cls : 'has-feedback',
9023 if (this.before || this.after) {
9026 cls : 'input-group',
9030 if (this.before && typeof(this.before) == 'string') {
9032 inputblock.cn.push({
9034 cls : 'roo-input-before input-group-addon',
9038 if (this.before && typeof(this.before) == 'object') {
9039 this.before = Roo.factory(this.before);
9041 inputblock.cn.push({
9043 cls : 'roo-input-before input-group-' +
9044 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9048 inputblock.cn.push(input);
9050 if (this.after && typeof(this.after) == 'string') {
9051 inputblock.cn.push({
9053 cls : 'roo-input-after input-group-addon',
9057 if (this.after && typeof(this.after) == 'object') {
9058 this.after = Roo.factory(this.after);
9060 inputblock.cn.push({
9062 cls : 'roo-input-after input-group-' +
9063 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9067 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9068 inputblock.cls += ' has-feedback';
9069 inputblock.cn.push(feedback);
9073 if (align ==='left' && this.fieldLabel.length) {
9075 cfg.cls += ' roo-form-group-label-left';
9080 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9081 tooltip : 'This field is required'
9086 cls : 'control-label',
9087 html : this.fieldLabel
9098 var labelCfg = cfg.cn[1];
9099 var contentCfg = cfg.cn[2];
9101 if(this.indicatorpos == 'right'){
9106 cls : 'control-label',
9110 html : this.fieldLabel
9114 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9115 tooltip : 'This field is required'
9128 labelCfg = cfg.cn[0];
9129 contentCfg = cfg.cn[1];
9133 if(this.labelWidth > 12){
9134 labelCfg.style = "width: " + this.labelWidth + 'px';
9137 if(this.labelWidth < 13 && this.labelmd == 0){
9138 this.labelmd = this.labelWidth;
9141 if(this.labellg > 0){
9142 labelCfg.cls += ' col-lg-' + this.labellg;
9143 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9146 if(this.labelmd > 0){
9147 labelCfg.cls += ' col-md-' + this.labelmd;
9148 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9151 if(this.labelsm > 0){
9152 labelCfg.cls += ' col-sm-' + this.labelsm;
9153 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9156 if(this.labelxs > 0){
9157 labelCfg.cls += ' col-xs-' + this.labelxs;
9158 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9162 } else if ( this.fieldLabel.length) {
9167 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9168 tooltip : 'This field is required'
9172 //cls : 'input-group-addon',
9173 html : this.fieldLabel
9181 if(this.indicatorpos == 'right'){
9186 //cls : 'input-group-addon',
9187 html : this.fieldLabel
9192 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9193 tooltip : 'This field is required'
9213 if (this.parentType === 'Navbar' && this.parent().bar) {
9214 cfg.cls += ' navbar-form';
9217 if (this.parentType === 'NavGroup') {
9218 cfg.cls += ' navbar-form';
9226 * return the real input element.
9228 inputEl: function ()
9230 return this.el.select('input.form-control',true).first();
9233 tooltipEl : function()
9235 return this.inputEl();
9238 indicatorEl : function()
9240 var indicator = this.el.select('i.roo-required-indicator',true).first();
9250 setDisabled : function(v)
9252 var i = this.inputEl().dom;
9254 i.removeAttribute('disabled');
9258 i.setAttribute('disabled','true');
9260 initEvents : function()
9263 this.inputEl().on("keydown" , this.fireKey, this);
9264 this.inputEl().on("focus", this.onFocus, this);
9265 this.inputEl().on("blur", this.onBlur, this);
9267 this.inputEl().relayEvent('keyup', this);
9269 this.indicator = this.indicatorEl();
9272 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9275 // reference to original value for reset
9276 this.originalValue = this.getValue();
9277 //Roo.form.TextField.superclass.initEvents.call(this);
9278 if(this.validationEvent == 'keyup'){
9279 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9280 this.inputEl().on('keyup', this.filterValidation, this);
9282 else if(this.validationEvent !== false){
9283 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9286 if(this.selectOnFocus){
9287 this.on("focus", this.preFocus, this);
9290 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9291 this.inputEl().on("keypress", this.filterKeys, this);
9293 this.inputEl().relayEvent('keypress', this);
9296 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9297 this.el.on("click", this.autoSize, this);
9300 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9301 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9304 if (typeof(this.before) == 'object') {
9305 this.before.render(this.el.select('.roo-input-before',true).first());
9307 if (typeof(this.after) == 'object') {
9308 this.after.render(this.el.select('.roo-input-after',true).first());
9311 this.inputEl().on('change', this.onChange, this);
9314 filterValidation : function(e){
9315 if(!e.isNavKeyPress()){
9316 this.validationTask.delay(this.validationDelay);
9320 * Validates the field value
9321 * @return {Boolean} True if the value is valid, else false
9323 validate : function(){
9324 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9325 if(this.disabled || this.validateValue(this.getRawValue())){
9336 * Validates a value according to the field's validation rules and marks the field as invalid
9337 * if the validation fails
9338 * @param {Mixed} value The value to validate
9339 * @return {Boolean} True if the value is valid, else false
9341 validateValue : function(value)
9343 if(this.getVisibilityEl().hasClass('hidden')){
9347 if(value.length < 1) { // if it's blank
9348 if(this.allowBlank){
9354 if(value.length < this.minLength){
9357 if(value.length > this.maxLength){
9361 var vt = Roo.form.VTypes;
9362 if(!vt[this.vtype](value, this)){
9366 if(typeof this.validator == "function"){
9367 var msg = this.validator(value);
9371 if (typeof(msg) == 'string') {
9372 this.invalidText = msg;
9376 if(this.regex && !this.regex.test(value)){
9384 fireKey : function(e){
9385 //Roo.log('field ' + e.getKey());
9386 if(e.isNavKeyPress()){
9387 this.fireEvent("specialkey", this, e);
9390 focus : function (selectText){
9392 this.inputEl().focus();
9393 if(selectText === true){
9394 this.inputEl().dom.select();
9400 onFocus : function(){
9401 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9402 // this.el.addClass(this.focusClass);
9405 this.hasFocus = true;
9406 this.startValue = this.getValue();
9407 this.fireEvent("focus", this);
9411 beforeBlur : Roo.emptyFn,
9415 onBlur : function(){
9417 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9418 //this.el.removeClass(this.focusClass);
9420 this.hasFocus = false;
9421 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9424 var v = this.getValue();
9425 if(String(v) !== String(this.startValue)){
9426 this.fireEvent('change', this, v, this.startValue);
9428 this.fireEvent("blur", this);
9431 onChange : function(e)
9433 var v = this.getValue();
9434 if(String(v) !== String(this.startValue)){
9435 this.fireEvent('change', this, v, this.startValue);
9441 * Resets the current field value to the originally loaded value and clears any validation messages
9444 this.setValue(this.originalValue);
9448 * Returns the name of the field
9449 * @return {Mixed} name The name field
9451 getName: function(){
9455 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9456 * @return {Mixed} value The field value
9458 getValue : function(){
9460 var v = this.inputEl().getValue();
9465 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9466 * @return {Mixed} value The field value
9468 getRawValue : function(){
9469 var v = this.inputEl().getValue();
9475 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9476 * @param {Mixed} value The value to set
9478 setRawValue : function(v){
9479 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9482 selectText : function(start, end){
9483 var v = this.getRawValue();
9485 start = start === undefined ? 0 : start;
9486 end = end === undefined ? v.length : end;
9487 var d = this.inputEl().dom;
9488 if(d.setSelectionRange){
9489 d.setSelectionRange(start, end);
9490 }else if(d.createTextRange){
9491 var range = d.createTextRange();
9492 range.moveStart("character", start);
9493 range.moveEnd("character", v.length-end);
9500 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9501 * @param {Mixed} value The value to set
9503 setValue : function(v){
9506 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9512 processValue : function(value){
9513 if(this.stripCharsRe){
9514 var newValue = value.replace(this.stripCharsRe, '');
9515 if(newValue !== value){
9516 this.setRawValue(newValue);
9523 preFocus : function(){
9525 if(this.selectOnFocus){
9526 this.inputEl().dom.select();
9529 filterKeys : function(e){
9531 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9534 var c = e.getCharCode(), cc = String.fromCharCode(c);
9535 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9538 if(!this.maskRe.test(cc)){
9543 * Clear any invalid styles/messages for this field
9545 clearInvalid : function(){
9547 if(!this.el || this.preventMark){ // not rendered
9552 this.el.removeClass(this.invalidClass);
9554 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9556 var feedback = this.el.select('.form-control-feedback', true).first();
9559 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9565 this.indicator.removeClass('visible');
9566 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9569 this.fireEvent('valid', this);
9573 * Mark this field as valid
9575 markValid : function()
9577 if(!this.el || this.preventMark){ // not rendered...
9581 this.el.removeClass([this.invalidClass, this.validClass]);
9583 var feedback = this.el.select('.form-control-feedback', true).first();
9586 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9590 this.indicator.removeClass('visible');
9591 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9598 if(this.allowBlank && !this.getRawValue().length){
9602 this.el.addClass(this.validClass);
9604 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9606 var feedback = this.el.select('.form-control-feedback', true).first();
9609 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9610 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9615 this.fireEvent('valid', this);
9619 * Mark this field as invalid
9620 * @param {String} msg The validation message
9622 markInvalid : function(msg)
9624 if(!this.el || this.preventMark){ // not rendered
9628 this.el.removeClass([this.invalidClass, this.validClass]);
9630 var feedback = this.el.select('.form-control-feedback', true).first();
9633 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9640 if(this.allowBlank && !this.getRawValue().length){
9645 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9646 this.indicator.addClass('visible');
9649 this.el.addClass(this.invalidClass);
9651 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9653 var feedback = this.el.select('.form-control-feedback', true).first();
9656 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9658 if(this.getValue().length || this.forceFeedback){
9659 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9666 this.fireEvent('invalid', this, msg);
9669 SafariOnKeyDown : function(event)
9671 // this is a workaround for a password hang bug on chrome/ webkit.
9672 if (this.inputEl().dom.type != 'password') {
9676 var isSelectAll = false;
9678 if(this.inputEl().dom.selectionEnd > 0){
9679 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9681 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9682 event.preventDefault();
9687 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9689 event.preventDefault();
9690 // this is very hacky as keydown always get's upper case.
9692 var cc = String.fromCharCode(event.getCharCode());
9693 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9697 adjustWidth : function(tag, w){
9698 tag = tag.toLowerCase();
9699 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9700 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9704 if(tag == 'textarea'){
9707 }else if(Roo.isOpera){
9711 if(tag == 'textarea'){
9719 setFieldLabel : function(v)
9726 var ar = this.el.select('label > span',true);
9728 if (ar.elements.length) {
9729 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9730 this.fieldLabel = v;
9734 var br = this.el.select('label',true);
9736 if(br.elements.length) {
9737 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9738 this.fieldLabel = v;
9742 Roo.log('Cannot Found any of label > span || label in input');
9746 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9747 this.fieldLabel = v;
9762 * @class Roo.bootstrap.TextArea
9763 * @extends Roo.bootstrap.Input
9764 * Bootstrap TextArea class
9765 * @cfg {Number} cols Specifies the visible width of a text area
9766 * @cfg {Number} rows Specifies the visible number of lines in a text area
9767 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9768 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9769 * @cfg {string} html text
9772 * Create a new TextArea
9773 * @param {Object} config The config object
9776 Roo.bootstrap.TextArea = function(config){
9777 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9781 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9791 getAutoCreate : function(){
9793 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9799 if(this.inputType != 'hidden'){
9800 cfg.cls = 'form-group' //input-group
9808 value : this.value || '',
9809 html: this.html || '',
9810 cls : 'form-control',
9811 placeholder : this.placeholder || ''
9815 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9816 input.maxLength = this.maxLength;
9820 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9824 input.cols = this.cols;
9827 if (this.readOnly) {
9828 input.readonly = true;
9832 input.name = this.name;
9836 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9840 ['xs','sm','md','lg'].map(function(size){
9841 if (settings[size]) {
9842 cfg.cls += ' col-' + size + '-' + settings[size];
9846 var inputblock = input;
9848 if(this.hasFeedback && !this.allowBlank){
9852 cls: 'glyphicon form-control-feedback'
9856 cls : 'has-feedback',
9865 if (this.before || this.after) {
9868 cls : 'input-group',
9872 inputblock.cn.push({
9874 cls : 'input-group-addon',
9879 inputblock.cn.push(input);
9881 if(this.hasFeedback && !this.allowBlank){
9882 inputblock.cls += ' has-feedback';
9883 inputblock.cn.push(feedback);
9887 inputblock.cn.push({
9889 cls : 'input-group-addon',
9896 if (align ==='left' && this.fieldLabel.length) {
9901 cls : 'control-label',
9902 html : this.fieldLabel
9913 if(this.labelWidth > 12){
9914 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9917 if(this.labelWidth < 13 && this.labelmd == 0){
9918 this.labelmd = this.labelWidth;
9921 if(this.labellg > 0){
9922 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9923 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9926 if(this.labelmd > 0){
9927 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9928 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9931 if(this.labelsm > 0){
9932 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9933 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9936 if(this.labelxs > 0){
9937 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9938 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9941 } else if ( this.fieldLabel.length) {
9946 //cls : 'input-group-addon',
9947 html : this.fieldLabel
9965 if (this.disabled) {
9966 input.disabled=true;
9973 * return the real textarea element.
9975 inputEl: function ()
9977 return this.el.select('textarea.form-control',true).first();
9981 * Clear any invalid styles/messages for this field
9983 clearInvalid : function()
9986 if(!this.el || this.preventMark){ // not rendered
9990 var label = this.el.select('label', true).first();
9991 var icon = this.el.select('i.fa-star', true).first();
9997 this.el.removeClass(this.invalidClass);
9999 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10001 var feedback = this.el.select('.form-control-feedback', true).first();
10004 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10009 this.fireEvent('valid', this);
10013 * Mark this field as valid
10015 markValid : function()
10017 if(!this.el || this.preventMark){ // not rendered
10021 this.el.removeClass([this.invalidClass, this.validClass]);
10023 var feedback = this.el.select('.form-control-feedback', true).first();
10026 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10029 if(this.disabled || this.allowBlank){
10033 var label = this.el.select('label', true).first();
10034 var icon = this.el.select('i.fa-star', true).first();
10040 this.el.addClass(this.validClass);
10042 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10044 var feedback = this.el.select('.form-control-feedback', true).first();
10047 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10048 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10053 this.fireEvent('valid', this);
10057 * Mark this field as invalid
10058 * @param {String} msg The validation message
10060 markInvalid : function(msg)
10062 if(!this.el || this.preventMark){ // not rendered
10066 this.el.removeClass([this.invalidClass, this.validClass]);
10068 var feedback = this.el.select('.form-control-feedback', true).first();
10071 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10074 if(this.disabled || this.allowBlank){
10078 var label = this.el.select('label', true).first();
10079 var icon = this.el.select('i.fa-star', true).first();
10081 if(!this.getValue().length && label && !icon){
10082 this.el.createChild({
10084 cls : 'text-danger fa fa-lg fa-star',
10085 tooltip : 'This field is required',
10086 style : 'margin-right:5px;'
10090 this.el.addClass(this.invalidClass);
10092 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10094 var feedback = this.el.select('.form-control-feedback', true).first();
10097 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10099 if(this.getValue().length || this.forceFeedback){
10100 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10107 this.fireEvent('invalid', this, msg);
10115 * trigger field - base class for combo..
10120 * @class Roo.bootstrap.TriggerField
10121 * @extends Roo.bootstrap.Input
10122 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10123 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10124 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10125 * for which you can provide a custom implementation. For example:
10127 var trigger = new Roo.bootstrap.TriggerField();
10128 trigger.onTriggerClick = myTriggerFn;
10129 trigger.applyTo('my-field');
10132 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10133 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10134 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10135 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10136 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10139 * Create a new TriggerField.
10140 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10141 * to the base TextField)
10143 Roo.bootstrap.TriggerField = function(config){
10144 this.mimicing = false;
10145 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10148 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10150 * @cfg {String} triggerClass A CSS class to apply to the trigger
10153 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10158 * @cfg {Boolean} removable (true|false) special filter default false
10162 /** @cfg {Boolean} grow @hide */
10163 /** @cfg {Number} growMin @hide */
10164 /** @cfg {Number} growMax @hide */
10170 autoSize: Roo.emptyFn,
10174 deferHeight : true,
10177 actionMode : 'wrap',
10182 getAutoCreate : function(){
10184 var align = this.labelAlign || this.parentLabelAlign();
10189 cls: 'form-group' //input-group
10196 type : this.inputType,
10197 cls : 'form-control',
10198 autocomplete: 'new-password',
10199 placeholder : this.placeholder || ''
10203 input.name = this.name;
10206 input.cls += ' input-' + this.size;
10209 if (this.disabled) {
10210 input.disabled=true;
10213 var inputblock = input;
10215 if(this.hasFeedback && !this.allowBlank){
10219 cls: 'glyphicon form-control-feedback'
10222 if(this.removable && !this.editable && !this.tickable){
10224 cls : 'has-feedback',
10230 cls : 'roo-combo-removable-btn close'
10237 cls : 'has-feedback',
10246 if(this.removable && !this.editable && !this.tickable){
10248 cls : 'roo-removable',
10254 cls : 'roo-combo-removable-btn close'
10261 if (this.before || this.after) {
10264 cls : 'input-group',
10268 inputblock.cn.push({
10270 cls : 'input-group-addon',
10275 inputblock.cn.push(input);
10277 if(this.hasFeedback && !this.allowBlank){
10278 inputblock.cls += ' has-feedback';
10279 inputblock.cn.push(feedback);
10283 inputblock.cn.push({
10285 cls : 'input-group-addon',
10298 cls: 'form-hidden-field'
10312 cls: 'form-hidden-field'
10316 cls: 'roo-select2-choices',
10320 cls: 'roo-select2-search-field',
10333 cls: 'roo-select2-container input-group',
10338 // cls: 'typeahead typeahead-long dropdown-menu',
10339 // style: 'display:none'
10344 if(!this.multiple && this.showToggleBtn){
10350 if (this.caret != false) {
10353 cls: 'fa fa-' + this.caret
10360 cls : 'input-group-addon btn dropdown-toggle',
10365 cls: 'combobox-clear',
10379 combobox.cls += ' roo-select2-container-multi';
10382 if (align ==='left' && this.fieldLabel.length) {
10384 cfg.cls += ' roo-form-group-label-left';
10389 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10390 tooltip : 'This field is required'
10395 cls : 'control-label',
10396 html : this.fieldLabel
10408 var labelCfg = cfg.cn[1];
10409 var contentCfg = cfg.cn[2];
10411 if(this.indicatorpos == 'right'){
10416 cls : 'control-label',
10420 html : this.fieldLabel
10424 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10425 tooltip : 'This field is required'
10438 labelCfg = cfg.cn[0];
10439 contentCfg = cfg.cn[1];
10442 if(this.labelWidth > 12){
10443 labelCfg.style = "width: " + this.labelWidth + 'px';
10446 if(this.labelWidth < 13 && this.labelmd == 0){
10447 this.labelmd = this.labelWidth;
10450 if(this.labellg > 0){
10451 labelCfg.cls += ' col-lg-' + this.labellg;
10452 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10455 if(this.labelmd > 0){
10456 labelCfg.cls += ' col-md-' + this.labelmd;
10457 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10460 if(this.labelsm > 0){
10461 labelCfg.cls += ' col-sm-' + this.labelsm;
10462 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10465 if(this.labelxs > 0){
10466 labelCfg.cls += ' col-xs-' + this.labelxs;
10467 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10470 } else if ( this.fieldLabel.length) {
10471 // Roo.log(" label");
10475 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10476 tooltip : 'This field is required'
10480 //cls : 'input-group-addon',
10481 html : this.fieldLabel
10489 if(this.indicatorpos == 'right'){
10497 html : this.fieldLabel
10501 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10502 tooltip : 'This field is required'
10515 // Roo.log(" no label && no align");
10522 ['xs','sm','md','lg'].map(function(size){
10523 if (settings[size]) {
10524 cfg.cls += ' col-' + size + '-' + settings[size];
10535 onResize : function(w, h){
10536 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10537 // if(typeof w == 'number'){
10538 // var x = w - this.trigger.getWidth();
10539 // this.inputEl().setWidth(this.adjustWidth('input', x));
10540 // this.trigger.setStyle('left', x+'px');
10545 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10548 getResizeEl : function(){
10549 return this.inputEl();
10553 getPositionEl : function(){
10554 return this.inputEl();
10558 alignErrorIcon : function(){
10559 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10563 initEvents : function(){
10567 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10568 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10569 if(!this.multiple && this.showToggleBtn){
10570 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10571 if(this.hideTrigger){
10572 this.trigger.setDisplayed(false);
10574 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10578 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10581 if(this.removable && !this.editable && !this.tickable){
10582 var close = this.closeTriggerEl();
10585 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10586 close.on('click', this.removeBtnClick, this, close);
10590 //this.trigger.addClassOnOver('x-form-trigger-over');
10591 //this.trigger.addClassOnClick('x-form-trigger-click');
10594 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10598 closeTriggerEl : function()
10600 var close = this.el.select('.roo-combo-removable-btn', true).first();
10601 return close ? close : false;
10604 removeBtnClick : function(e, h, el)
10606 e.preventDefault();
10608 if(this.fireEvent("remove", this) !== false){
10610 this.fireEvent("afterremove", this)
10614 createList : function()
10616 this.list = Roo.get(document.body).createChild({
10618 cls: 'typeahead typeahead-long dropdown-menu',
10619 style: 'display:none'
10622 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10627 initTrigger : function(){
10632 onDestroy : function(){
10634 this.trigger.removeAllListeners();
10635 // this.trigger.remove();
10638 // this.wrap.remove();
10640 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10644 onFocus : function(){
10645 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10647 if(!this.mimicing){
10648 this.wrap.addClass('x-trigger-wrap-focus');
10649 this.mimicing = true;
10650 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10651 if(this.monitorTab){
10652 this.el.on("keydown", this.checkTab, this);
10659 checkTab : function(e){
10660 if(e.getKey() == e.TAB){
10661 this.triggerBlur();
10666 onBlur : function(){
10671 mimicBlur : function(e, t){
10673 if(!this.wrap.contains(t) && this.validateBlur()){
10674 this.triggerBlur();
10680 triggerBlur : function(){
10681 this.mimicing = false;
10682 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10683 if(this.monitorTab){
10684 this.el.un("keydown", this.checkTab, this);
10686 //this.wrap.removeClass('x-trigger-wrap-focus');
10687 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10691 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10692 validateBlur : function(e, t){
10697 onDisable : function(){
10698 this.inputEl().dom.disabled = true;
10699 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10701 // this.wrap.addClass('x-item-disabled');
10706 onEnable : function(){
10707 this.inputEl().dom.disabled = false;
10708 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10710 // this.el.removeClass('x-item-disabled');
10715 onShow : function(){
10716 var ae = this.getActionEl();
10719 ae.dom.style.display = '';
10720 ae.dom.style.visibility = 'visible';
10726 onHide : function(){
10727 var ae = this.getActionEl();
10728 ae.dom.style.display = 'none';
10732 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10733 * by an implementing function.
10735 * @param {EventObject} e
10737 onTriggerClick : Roo.emptyFn
10741 * Ext JS Library 1.1.1
10742 * Copyright(c) 2006-2007, Ext JS, LLC.
10744 * Originally Released Under LGPL - original licence link has changed is not relivant.
10747 * <script type="text/javascript">
10752 * @class Roo.data.SortTypes
10754 * Defines the default sorting (casting?) comparison functions used when sorting data.
10756 Roo.data.SortTypes = {
10758 * Default sort that does nothing
10759 * @param {Mixed} s The value being converted
10760 * @return {Mixed} The comparison value
10762 none : function(s){
10767 * The regular expression used to strip tags
10771 stripTagsRE : /<\/?[^>]+>/gi,
10774 * Strips all HTML tags to sort on text only
10775 * @param {Mixed} s The value being converted
10776 * @return {String} The comparison value
10778 asText : function(s){
10779 return String(s).replace(this.stripTagsRE, "");
10783 * Strips all HTML tags to sort on text only - Case insensitive
10784 * @param {Mixed} s The value being converted
10785 * @return {String} The comparison value
10787 asUCText : function(s){
10788 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10792 * Case insensitive string
10793 * @param {Mixed} s The value being converted
10794 * @return {String} The comparison value
10796 asUCString : function(s) {
10797 return String(s).toUpperCase();
10802 * @param {Mixed} s The value being converted
10803 * @return {Number} The comparison value
10805 asDate : function(s) {
10809 if(s instanceof Date){
10810 return s.getTime();
10812 return Date.parse(String(s));
10817 * @param {Mixed} s The value being converted
10818 * @return {Float} The comparison value
10820 asFloat : function(s) {
10821 var val = parseFloat(String(s).replace(/,/g, ""));
10830 * @param {Mixed} s The value being converted
10831 * @return {Number} The comparison value
10833 asInt : function(s) {
10834 var val = parseInt(String(s).replace(/,/g, ""));
10842 * Ext JS Library 1.1.1
10843 * Copyright(c) 2006-2007, Ext JS, LLC.
10845 * Originally Released Under LGPL - original licence link has changed is not relivant.
10848 * <script type="text/javascript">
10852 * @class Roo.data.Record
10853 * Instances of this class encapsulate both record <em>definition</em> information, and record
10854 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10855 * to access Records cached in an {@link Roo.data.Store} object.<br>
10857 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10858 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10861 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10863 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10864 * {@link #create}. The parameters are the same.
10865 * @param {Array} data An associative Array of data values keyed by the field name.
10866 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10867 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10868 * not specified an integer id is generated.
10870 Roo.data.Record = function(data, id){
10871 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10876 * Generate a constructor for a specific record layout.
10877 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10878 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10879 * Each field definition object may contain the following properties: <ul>
10880 * <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,
10881 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10882 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10883 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10884 * is being used, then this is a string containing the javascript expression to reference the data relative to
10885 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10886 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10887 * this may be omitted.</p></li>
10888 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10889 * <ul><li>auto (Default, implies no conversion)</li>
10894 * <li>date</li></ul></p></li>
10895 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10896 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10897 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10898 * by the Reader into an object that will be stored in the Record. It is passed the
10899 * following parameters:<ul>
10900 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10902 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10904 * <br>usage:<br><pre><code>
10905 var TopicRecord = Roo.data.Record.create(
10906 {name: 'title', mapping: 'topic_title'},
10907 {name: 'author', mapping: 'username'},
10908 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10909 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10910 {name: 'lastPoster', mapping: 'user2'},
10911 {name: 'excerpt', mapping: 'post_text'}
10914 var myNewRecord = new TopicRecord({
10915 title: 'Do my job please',
10918 lastPost: new Date(),
10919 lastPoster: 'Animal',
10920 excerpt: 'No way dude!'
10922 myStore.add(myNewRecord);
10927 Roo.data.Record.create = function(o){
10928 var f = function(){
10929 f.superclass.constructor.apply(this, arguments);
10931 Roo.extend(f, Roo.data.Record);
10932 var p = f.prototype;
10933 p.fields = new Roo.util.MixedCollection(false, function(field){
10936 for(var i = 0, len = o.length; i < len; i++){
10937 p.fields.add(new Roo.data.Field(o[i]));
10939 f.getField = function(name){
10940 return p.fields.get(name);
10945 Roo.data.Record.AUTO_ID = 1000;
10946 Roo.data.Record.EDIT = 'edit';
10947 Roo.data.Record.REJECT = 'reject';
10948 Roo.data.Record.COMMIT = 'commit';
10950 Roo.data.Record.prototype = {
10952 * Readonly flag - true if this record has been modified.
10961 join : function(store){
10962 this.store = store;
10966 * Set the named field to the specified value.
10967 * @param {String} name The name of the field to set.
10968 * @param {Object} value The value to set the field to.
10970 set : function(name, value){
10971 if(this.data[name] == value){
10975 if(!this.modified){
10976 this.modified = {};
10978 if(typeof this.modified[name] == 'undefined'){
10979 this.modified[name] = this.data[name];
10981 this.data[name] = value;
10982 if(!this.editing && this.store){
10983 this.store.afterEdit(this);
10988 * Get the value of the named field.
10989 * @param {String} name The name of the field to get the value of.
10990 * @return {Object} The value of the field.
10992 get : function(name){
10993 return this.data[name];
10997 beginEdit : function(){
10998 this.editing = true;
10999 this.modified = {};
11003 cancelEdit : function(){
11004 this.editing = false;
11005 delete this.modified;
11009 endEdit : function(){
11010 this.editing = false;
11011 if(this.dirty && this.store){
11012 this.store.afterEdit(this);
11017 * Usually called by the {@link Roo.data.Store} which owns the Record.
11018 * Rejects all changes made to the Record since either creation, or the last commit operation.
11019 * Modified fields are reverted to their original values.
11021 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11022 * of reject operations.
11024 reject : function(){
11025 var m = this.modified;
11027 if(typeof m[n] != "function"){
11028 this.data[n] = m[n];
11031 this.dirty = false;
11032 delete this.modified;
11033 this.editing = false;
11035 this.store.afterReject(this);
11040 * Usually called by the {@link Roo.data.Store} which owns the Record.
11041 * Commits all changes made to the Record since either creation, or the last commit operation.
11043 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11044 * of commit operations.
11046 commit : function(){
11047 this.dirty = false;
11048 delete this.modified;
11049 this.editing = false;
11051 this.store.afterCommit(this);
11056 hasError : function(){
11057 return this.error != null;
11061 clearError : function(){
11066 * Creates a copy of this record.
11067 * @param {String} id (optional) A new record id if you don't want to use this record's id
11070 copy : function(newId) {
11071 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11075 * Ext JS Library 1.1.1
11076 * Copyright(c) 2006-2007, Ext JS, LLC.
11078 * Originally Released Under LGPL - original licence link has changed is not relivant.
11081 * <script type="text/javascript">
11087 * @class Roo.data.Store
11088 * @extends Roo.util.Observable
11089 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11090 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11092 * 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
11093 * has no knowledge of the format of the data returned by the Proxy.<br>
11095 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11096 * instances from the data object. These records are cached and made available through accessor functions.
11098 * Creates a new Store.
11099 * @param {Object} config A config object containing the objects needed for the Store to access data,
11100 * and read the data into Records.
11102 Roo.data.Store = function(config){
11103 this.data = new Roo.util.MixedCollection(false);
11104 this.data.getKey = function(o){
11107 this.baseParams = {};
11109 this.paramNames = {
11114 "multisort" : "_multisort"
11117 if(config && config.data){
11118 this.inlineData = config.data;
11119 delete config.data;
11122 Roo.apply(this, config);
11124 if(this.reader){ // reader passed
11125 this.reader = Roo.factory(this.reader, Roo.data);
11126 this.reader.xmodule = this.xmodule || false;
11127 if(!this.recordType){
11128 this.recordType = this.reader.recordType;
11130 if(this.reader.onMetaChange){
11131 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11135 if(this.recordType){
11136 this.fields = this.recordType.prototype.fields;
11138 this.modified = [];
11142 * @event datachanged
11143 * Fires when the data cache has changed, and a widget which is using this Store
11144 * as a Record cache should refresh its view.
11145 * @param {Store} this
11147 datachanged : true,
11149 * @event metachange
11150 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11151 * @param {Store} this
11152 * @param {Object} meta The JSON metadata
11157 * Fires when Records have been added to the Store
11158 * @param {Store} this
11159 * @param {Roo.data.Record[]} records The array of Records added
11160 * @param {Number} index The index at which the record(s) were added
11165 * Fires when a Record has been removed from the Store
11166 * @param {Store} this
11167 * @param {Roo.data.Record} record The Record that was removed
11168 * @param {Number} index The index at which the record was removed
11173 * Fires when a Record has been updated
11174 * @param {Store} this
11175 * @param {Roo.data.Record} record The Record that was updated
11176 * @param {String} operation The update operation being performed. Value may be one of:
11178 Roo.data.Record.EDIT
11179 Roo.data.Record.REJECT
11180 Roo.data.Record.COMMIT
11186 * Fires when the data cache has been cleared.
11187 * @param {Store} this
11191 * @event beforeload
11192 * Fires before a request is made for a new data object. If the beforeload handler returns false
11193 * the load action will be canceled.
11194 * @param {Store} this
11195 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11199 * @event beforeloadadd
11200 * Fires after a new set of Records has been loaded.
11201 * @param {Store} this
11202 * @param {Roo.data.Record[]} records The Records that were loaded
11203 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11205 beforeloadadd : true,
11208 * Fires after a new set of Records has been loaded, before they are added to the store.
11209 * @param {Store} this
11210 * @param {Roo.data.Record[]} records The Records that were loaded
11211 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11212 * @params {Object} return from reader
11216 * @event loadexception
11217 * Fires if an exception occurs in the Proxy during loading.
11218 * Called with the signature of the Proxy's "loadexception" event.
11219 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11222 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11223 * @param {Object} load options
11224 * @param {Object} jsonData from your request (normally this contains the Exception)
11226 loadexception : true
11230 this.proxy = Roo.factory(this.proxy, Roo.data);
11231 this.proxy.xmodule = this.xmodule || false;
11232 this.relayEvents(this.proxy, ["loadexception"]);
11234 this.sortToggle = {};
11235 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11237 Roo.data.Store.superclass.constructor.call(this);
11239 if(this.inlineData){
11240 this.loadData(this.inlineData);
11241 delete this.inlineData;
11245 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11247 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11248 * without a remote query - used by combo/forms at present.
11252 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11255 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11258 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11259 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11262 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11263 * on any HTTP request
11266 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11269 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11273 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11274 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11276 remoteSort : false,
11279 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11280 * loaded or when a record is removed. (defaults to false).
11282 pruneModifiedRecords : false,
11285 lastOptions : null,
11288 * Add Records to the Store and fires the add event.
11289 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11291 add : function(records){
11292 records = [].concat(records);
11293 for(var i = 0, len = records.length; i < len; i++){
11294 records[i].join(this);
11296 var index = this.data.length;
11297 this.data.addAll(records);
11298 this.fireEvent("add", this, records, index);
11302 * Remove a Record from the Store and fires the remove event.
11303 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11305 remove : function(record){
11306 var index = this.data.indexOf(record);
11307 this.data.removeAt(index);
11309 if(this.pruneModifiedRecords){
11310 this.modified.remove(record);
11312 this.fireEvent("remove", this, record, index);
11316 * Remove all Records from the Store and fires the clear event.
11318 removeAll : function(){
11320 if(this.pruneModifiedRecords){
11321 this.modified = [];
11323 this.fireEvent("clear", this);
11327 * Inserts Records to the Store at the given index and fires the add event.
11328 * @param {Number} index The start index at which to insert the passed Records.
11329 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11331 insert : function(index, records){
11332 records = [].concat(records);
11333 for(var i = 0, len = records.length; i < len; i++){
11334 this.data.insert(index, records[i]);
11335 records[i].join(this);
11337 this.fireEvent("add", this, records, index);
11341 * Get the index within the cache of the passed Record.
11342 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11343 * @return {Number} The index of the passed Record. Returns -1 if not found.
11345 indexOf : function(record){
11346 return this.data.indexOf(record);
11350 * Get the index within the cache of the Record with the passed id.
11351 * @param {String} id The id of the Record to find.
11352 * @return {Number} The index of the Record. Returns -1 if not found.
11354 indexOfId : function(id){
11355 return this.data.indexOfKey(id);
11359 * Get the Record with the specified id.
11360 * @param {String} id The id of the Record to find.
11361 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11363 getById : function(id){
11364 return this.data.key(id);
11368 * Get the Record at the specified index.
11369 * @param {Number} index The index of the Record to find.
11370 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11372 getAt : function(index){
11373 return this.data.itemAt(index);
11377 * Returns a range of Records between specified indices.
11378 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11379 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11380 * @return {Roo.data.Record[]} An array of Records
11382 getRange : function(start, end){
11383 return this.data.getRange(start, end);
11387 storeOptions : function(o){
11388 o = Roo.apply({}, o);
11391 this.lastOptions = o;
11395 * Loads the Record cache from the configured Proxy using the configured Reader.
11397 * If using remote paging, then the first load call must specify the <em>start</em>
11398 * and <em>limit</em> properties in the options.params property to establish the initial
11399 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11401 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11402 * and this call will return before the new data has been loaded. Perform any post-processing
11403 * in a callback function, or in a "load" event handler.</strong>
11405 * @param {Object} options An object containing properties which control loading options:<ul>
11406 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11407 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11408 * passed the following arguments:<ul>
11409 * <li>r : Roo.data.Record[]</li>
11410 * <li>options: Options object from the load call</li>
11411 * <li>success: Boolean success indicator</li></ul></li>
11412 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11413 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11416 load : function(options){
11417 options = options || {};
11418 if(this.fireEvent("beforeload", this, options) !== false){
11419 this.storeOptions(options);
11420 var p = Roo.apply(options.params || {}, this.baseParams);
11421 // if meta was not loaded from remote source.. try requesting it.
11422 if (!this.reader.metaFromRemote) {
11423 p._requestMeta = 1;
11425 if(this.sortInfo && this.remoteSort){
11426 var pn = this.paramNames;
11427 p[pn["sort"]] = this.sortInfo.field;
11428 p[pn["dir"]] = this.sortInfo.direction;
11430 if (this.multiSort) {
11431 var pn = this.paramNames;
11432 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11435 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11440 * Reloads the Record cache from the configured Proxy using the configured Reader and
11441 * the options from the last load operation performed.
11442 * @param {Object} options (optional) An object containing properties which may override the options
11443 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11444 * the most recently used options are reused).
11446 reload : function(options){
11447 this.load(Roo.applyIf(options||{}, this.lastOptions));
11451 // Called as a callback by the Reader during a load operation.
11452 loadRecords : function(o, options, success){
11453 if(!o || success === false){
11454 if(success !== false){
11455 this.fireEvent("load", this, [], options, o);
11457 if(options.callback){
11458 options.callback.call(options.scope || this, [], options, false);
11462 // if data returned failure - throw an exception.
11463 if (o.success === false) {
11464 // show a message if no listener is registered.
11465 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11466 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11468 // loadmask wil be hooked into this..
11469 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11472 var r = o.records, t = o.totalRecords || r.length;
11474 this.fireEvent("beforeloadadd", this, r, options, o);
11476 if(!options || options.add !== true){
11477 if(this.pruneModifiedRecords){
11478 this.modified = [];
11480 for(var i = 0, len = r.length; i < len; i++){
11484 this.data = this.snapshot;
11485 delete this.snapshot;
11488 this.data.addAll(r);
11489 this.totalLength = t;
11491 this.fireEvent("datachanged", this);
11493 this.totalLength = Math.max(t, this.data.length+r.length);
11497 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11499 var e = new Roo.data.Record({});
11501 e.set(this.parent.displayField, this.parent.emptyTitle);
11502 e.set(this.parent.valueField, '');
11507 this.fireEvent("load", this, r, options, o);
11508 if(options.callback){
11509 options.callback.call(options.scope || this, r, options, true);
11515 * Loads data from a passed data block. A Reader which understands the format of the data
11516 * must have been configured in the constructor.
11517 * @param {Object} data The data block from which to read the Records. The format of the data expected
11518 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11519 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11521 loadData : function(o, append){
11522 var r = this.reader.readRecords(o);
11523 this.loadRecords(r, {add: append}, true);
11527 * Gets the number of cached records.
11529 * <em>If using paging, this may not be the total size of the dataset. If the data object
11530 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11531 * the data set size</em>
11533 getCount : function(){
11534 return this.data.length || 0;
11538 * Gets the total number of records in the dataset as returned by the server.
11540 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11541 * the dataset size</em>
11543 getTotalCount : function(){
11544 return this.totalLength || 0;
11548 * Returns the sort state of the Store as an object with two properties:
11550 field {String} The name of the field by which the Records are sorted
11551 direction {String} The sort order, "ASC" or "DESC"
11554 getSortState : function(){
11555 return this.sortInfo;
11559 applySort : function(){
11560 if(this.sortInfo && !this.remoteSort){
11561 var s = this.sortInfo, f = s.field;
11562 var st = this.fields.get(f).sortType;
11563 var fn = function(r1, r2){
11564 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11565 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11567 this.data.sort(s.direction, fn);
11568 if(this.snapshot && this.snapshot != this.data){
11569 this.snapshot.sort(s.direction, fn);
11575 * Sets the default sort column and order to be used by the next load operation.
11576 * @param {String} fieldName The name of the field to sort by.
11577 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11579 setDefaultSort : function(field, dir){
11580 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11584 * Sort the Records.
11585 * If remote sorting is used, the sort is performed on the server, and the cache is
11586 * reloaded. If local sorting is used, the cache is sorted internally.
11587 * @param {String} fieldName The name of the field to sort by.
11588 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11590 sort : function(fieldName, dir){
11591 var f = this.fields.get(fieldName);
11593 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11595 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11596 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11601 this.sortToggle[f.name] = dir;
11602 this.sortInfo = {field: f.name, direction: dir};
11603 if(!this.remoteSort){
11605 this.fireEvent("datachanged", this);
11607 this.load(this.lastOptions);
11612 * Calls the specified function for each of the Records in the cache.
11613 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11614 * Returning <em>false</em> aborts and exits the iteration.
11615 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11617 each : function(fn, scope){
11618 this.data.each(fn, scope);
11622 * Gets all records modified since the last commit. Modified records are persisted across load operations
11623 * (e.g., during paging).
11624 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11626 getModifiedRecords : function(){
11627 return this.modified;
11631 createFilterFn : function(property, value, anyMatch){
11632 if(!value.exec){ // not a regex
11633 value = String(value);
11634 if(value.length == 0){
11637 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11639 return function(r){
11640 return value.test(r.data[property]);
11645 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11646 * @param {String} property A field on your records
11647 * @param {Number} start The record index to start at (defaults to 0)
11648 * @param {Number} end The last record index to include (defaults to length - 1)
11649 * @return {Number} The sum
11651 sum : function(property, start, end){
11652 var rs = this.data.items, v = 0;
11653 start = start || 0;
11654 end = (end || end === 0) ? end : rs.length-1;
11656 for(var i = start; i <= end; i++){
11657 v += (rs[i].data[property] || 0);
11663 * Filter the records by a specified property.
11664 * @param {String} field A field on your records
11665 * @param {String/RegExp} value Either a string that the field
11666 * should start with or a RegExp to test against the field
11667 * @param {Boolean} anyMatch True to match any part not just the beginning
11669 filter : function(property, value, anyMatch){
11670 var fn = this.createFilterFn(property, value, anyMatch);
11671 return fn ? this.filterBy(fn) : this.clearFilter();
11675 * Filter by a function. The specified function will be called with each
11676 * record in this data source. If the function returns true the record is included,
11677 * otherwise it is filtered.
11678 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11679 * @param {Object} scope (optional) The scope of the function (defaults to this)
11681 filterBy : function(fn, scope){
11682 this.snapshot = this.snapshot || this.data;
11683 this.data = this.queryBy(fn, scope||this);
11684 this.fireEvent("datachanged", this);
11688 * Query the records by a specified property.
11689 * @param {String} field A field on your records
11690 * @param {String/RegExp} value Either a string that the field
11691 * should start with or a RegExp to test against the field
11692 * @param {Boolean} anyMatch True to match any part not just the beginning
11693 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11695 query : function(property, value, anyMatch){
11696 var fn = this.createFilterFn(property, value, anyMatch);
11697 return fn ? this.queryBy(fn) : this.data.clone();
11701 * Query by a function. The specified function will be called with each
11702 * record in this data source. If the function returns true the record is included
11704 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11705 * @param {Object} scope (optional) The scope of the function (defaults to this)
11706 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11708 queryBy : function(fn, scope){
11709 var data = this.snapshot || this.data;
11710 return data.filterBy(fn, scope||this);
11714 * Collects unique values for a particular dataIndex from this store.
11715 * @param {String} dataIndex The property to collect
11716 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11717 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11718 * @return {Array} An array of the unique values
11720 collect : function(dataIndex, allowNull, bypassFilter){
11721 var d = (bypassFilter === true && this.snapshot) ?
11722 this.snapshot.items : this.data.items;
11723 var v, sv, r = [], l = {};
11724 for(var i = 0, len = d.length; i < len; i++){
11725 v = d[i].data[dataIndex];
11727 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11736 * Revert to a view of the Record cache with no filtering applied.
11737 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11739 clearFilter : function(suppressEvent){
11740 if(this.snapshot && this.snapshot != this.data){
11741 this.data = this.snapshot;
11742 delete this.snapshot;
11743 if(suppressEvent !== true){
11744 this.fireEvent("datachanged", this);
11750 afterEdit : function(record){
11751 if(this.modified.indexOf(record) == -1){
11752 this.modified.push(record);
11754 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11758 afterReject : function(record){
11759 this.modified.remove(record);
11760 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11764 afterCommit : function(record){
11765 this.modified.remove(record);
11766 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11770 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11771 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11773 commitChanges : function(){
11774 var m = this.modified.slice(0);
11775 this.modified = [];
11776 for(var i = 0, len = m.length; i < len; i++){
11782 * Cancel outstanding changes on all changed records.
11784 rejectChanges : function(){
11785 var m = this.modified.slice(0);
11786 this.modified = [];
11787 for(var i = 0, len = m.length; i < len; i++){
11792 onMetaChange : function(meta, rtype, o){
11793 this.recordType = rtype;
11794 this.fields = rtype.prototype.fields;
11795 delete this.snapshot;
11796 this.sortInfo = meta.sortInfo || this.sortInfo;
11797 this.modified = [];
11798 this.fireEvent('metachange', this, this.reader.meta);
11801 moveIndex : function(data, type)
11803 var index = this.indexOf(data);
11805 var newIndex = index + type;
11809 this.insert(newIndex, data);
11814 * Ext JS Library 1.1.1
11815 * Copyright(c) 2006-2007, Ext JS, LLC.
11817 * Originally Released Under LGPL - original licence link has changed is not relivant.
11820 * <script type="text/javascript">
11824 * @class Roo.data.SimpleStore
11825 * @extends Roo.data.Store
11826 * Small helper class to make creating Stores from Array data easier.
11827 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11828 * @cfg {Array} fields An array of field definition objects, or field name strings.
11829 * @cfg {Array} data The multi-dimensional array of data
11831 * @param {Object} config
11833 Roo.data.SimpleStore = function(config){
11834 Roo.data.SimpleStore.superclass.constructor.call(this, {
11836 reader: new Roo.data.ArrayReader({
11839 Roo.data.Record.create(config.fields)
11841 proxy : new Roo.data.MemoryProxy(config.data)
11845 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11847 * Ext JS Library 1.1.1
11848 * Copyright(c) 2006-2007, Ext JS, LLC.
11850 * Originally Released Under LGPL - original licence link has changed is not relivant.
11853 * <script type="text/javascript">
11858 * @extends Roo.data.Store
11859 * @class Roo.data.JsonStore
11860 * Small helper class to make creating Stores for JSON data easier. <br/>
11862 var store = new Roo.data.JsonStore({
11863 url: 'get-images.php',
11865 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11868 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11869 * JsonReader and HttpProxy (unless inline data is provided).</b>
11870 * @cfg {Array} fields An array of field definition objects, or field name strings.
11872 * @param {Object} config
11874 Roo.data.JsonStore = function(c){
11875 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11876 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11877 reader: new Roo.data.JsonReader(c, c.fields)
11880 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11882 * Ext JS Library 1.1.1
11883 * Copyright(c) 2006-2007, Ext JS, LLC.
11885 * Originally Released Under LGPL - original licence link has changed is not relivant.
11888 * <script type="text/javascript">
11892 Roo.data.Field = function(config){
11893 if(typeof config == "string"){
11894 config = {name: config};
11896 Roo.apply(this, config);
11899 this.type = "auto";
11902 var st = Roo.data.SortTypes;
11903 // named sortTypes are supported, here we look them up
11904 if(typeof this.sortType == "string"){
11905 this.sortType = st[this.sortType];
11908 // set default sortType for strings and dates
11909 if(!this.sortType){
11912 this.sortType = st.asUCString;
11915 this.sortType = st.asDate;
11918 this.sortType = st.none;
11923 var stripRe = /[\$,%]/g;
11925 // prebuilt conversion function for this field, instead of
11926 // switching every time we're reading a value
11928 var cv, dateFormat = this.dateFormat;
11933 cv = function(v){ return v; };
11936 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11940 return v !== undefined && v !== null && v !== '' ?
11941 parseInt(String(v).replace(stripRe, ""), 10) : '';
11946 return v !== undefined && v !== null && v !== '' ?
11947 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11952 cv = function(v){ return v === true || v === "true" || v == 1; };
11959 if(v instanceof Date){
11963 if(dateFormat == "timestamp"){
11964 return new Date(v*1000);
11966 return Date.parseDate(v, dateFormat);
11968 var parsed = Date.parse(v);
11969 return parsed ? new Date(parsed) : null;
11978 Roo.data.Field.prototype = {
11986 * Ext JS Library 1.1.1
11987 * Copyright(c) 2006-2007, Ext JS, LLC.
11989 * Originally Released Under LGPL - original licence link has changed is not relivant.
11992 * <script type="text/javascript">
11995 // Base class for reading structured data from a data source. This class is intended to be
11996 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11999 * @class Roo.data.DataReader
12000 * Base class for reading structured data from a data source. This class is intended to be
12001 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12004 Roo.data.DataReader = function(meta, recordType){
12008 this.recordType = recordType instanceof Array ?
12009 Roo.data.Record.create(recordType) : recordType;
12012 Roo.data.DataReader.prototype = {
12014 * Create an empty record
12015 * @param {Object} data (optional) - overlay some values
12016 * @return {Roo.data.Record} record created.
12018 newRow : function(d) {
12020 this.recordType.prototype.fields.each(function(c) {
12022 case 'int' : da[c.name] = 0; break;
12023 case 'date' : da[c.name] = new Date(); break;
12024 case 'float' : da[c.name] = 0.0; break;
12025 case 'boolean' : da[c.name] = false; break;
12026 default : da[c.name] = ""; break;
12030 return new this.recordType(Roo.apply(da, d));
12035 * Ext JS Library 1.1.1
12036 * Copyright(c) 2006-2007, Ext JS, LLC.
12038 * Originally Released Under LGPL - original licence link has changed is not relivant.
12041 * <script type="text/javascript">
12045 * @class Roo.data.DataProxy
12046 * @extends Roo.data.Observable
12047 * This class is an abstract base class for implementations which provide retrieval of
12048 * unformatted data objects.<br>
12050 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12051 * (of the appropriate type which knows how to parse the data object) to provide a block of
12052 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12054 * Custom implementations must implement the load method as described in
12055 * {@link Roo.data.HttpProxy#load}.
12057 Roo.data.DataProxy = function(){
12060 * @event beforeload
12061 * Fires before a network request is made to retrieve a data object.
12062 * @param {Object} This DataProxy object.
12063 * @param {Object} params The params parameter to the load function.
12068 * Fires before the load method's callback is called.
12069 * @param {Object} This DataProxy object.
12070 * @param {Object} o The data object.
12071 * @param {Object} arg The callback argument object passed to the load function.
12075 * @event loadexception
12076 * Fires if an Exception occurs during data retrieval.
12077 * @param {Object} This DataProxy object.
12078 * @param {Object} o The data object.
12079 * @param {Object} arg The callback argument object passed to the load function.
12080 * @param {Object} e The Exception.
12082 loadexception : true
12084 Roo.data.DataProxy.superclass.constructor.call(this);
12087 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12090 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12094 * Ext JS Library 1.1.1
12095 * Copyright(c) 2006-2007, Ext JS, LLC.
12097 * Originally Released Under LGPL - original licence link has changed is not relivant.
12100 * <script type="text/javascript">
12103 * @class Roo.data.MemoryProxy
12104 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12105 * to the Reader when its load method is called.
12107 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12109 Roo.data.MemoryProxy = function(data){
12113 Roo.data.MemoryProxy.superclass.constructor.call(this);
12117 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12120 * Load data from the requested source (in this case an in-memory
12121 * data object passed to the constructor), read the data object into
12122 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12123 * process that block using the passed callback.
12124 * @param {Object} params This parameter is not used by the MemoryProxy class.
12125 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12126 * object into a block of Roo.data.Records.
12127 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12128 * The function must be passed <ul>
12129 * <li>The Record block object</li>
12130 * <li>The "arg" argument from the load function</li>
12131 * <li>A boolean success indicator</li>
12133 * @param {Object} scope The scope in which to call the callback
12134 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12136 load : function(params, reader, callback, scope, arg){
12137 params = params || {};
12140 result = reader.readRecords(this.data);
12142 this.fireEvent("loadexception", this, arg, null, e);
12143 callback.call(scope, null, arg, false);
12146 callback.call(scope, result, arg, true);
12150 update : function(params, records){
12155 * Ext JS Library 1.1.1
12156 * Copyright(c) 2006-2007, Ext JS, LLC.
12158 * Originally Released Under LGPL - original licence link has changed is not relivant.
12161 * <script type="text/javascript">
12164 * @class Roo.data.HttpProxy
12165 * @extends Roo.data.DataProxy
12166 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12167 * configured to reference a certain URL.<br><br>
12169 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12170 * from which the running page was served.<br><br>
12172 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12174 * Be aware that to enable the browser to parse an XML document, the server must set
12175 * the Content-Type header in the HTTP response to "text/xml".
12177 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12178 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12179 * will be used to make the request.
12181 Roo.data.HttpProxy = function(conn){
12182 Roo.data.HttpProxy.superclass.constructor.call(this);
12183 // is conn a conn config or a real conn?
12185 this.useAjax = !conn || !conn.events;
12189 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12190 // thse are take from connection...
12193 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12196 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12197 * extra parameters to each request made by this object. (defaults to undefined)
12200 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12201 * to each request made by this object. (defaults to undefined)
12204 * @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)
12207 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12210 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12216 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12220 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12221 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12222 * a finer-grained basis than the DataProxy events.
12224 getConnection : function(){
12225 return this.useAjax ? Roo.Ajax : this.conn;
12229 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12230 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12231 * process that block using the passed callback.
12232 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12233 * for the request to the remote server.
12234 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12235 * object into a block of Roo.data.Records.
12236 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12237 * The function must be passed <ul>
12238 * <li>The Record block object</li>
12239 * <li>The "arg" argument from the load function</li>
12240 * <li>A boolean success indicator</li>
12242 * @param {Object} scope The scope in which to call the callback
12243 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12245 load : function(params, reader, callback, scope, arg){
12246 if(this.fireEvent("beforeload", this, params) !== false){
12248 params : params || {},
12250 callback : callback,
12255 callback : this.loadResponse,
12259 Roo.applyIf(o, this.conn);
12260 if(this.activeRequest){
12261 Roo.Ajax.abort(this.activeRequest);
12263 this.activeRequest = Roo.Ajax.request(o);
12265 this.conn.request(o);
12268 callback.call(scope||this, null, arg, false);
12273 loadResponse : function(o, success, response){
12274 delete this.activeRequest;
12276 this.fireEvent("loadexception", this, o, response);
12277 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12282 result = o.reader.read(response);
12284 this.fireEvent("loadexception", this, o, response, e);
12285 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12289 this.fireEvent("load", this, o, o.request.arg);
12290 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12294 update : function(dataSet){
12299 updateResponse : function(dataSet){
12304 * Ext JS Library 1.1.1
12305 * Copyright(c) 2006-2007, Ext JS, LLC.
12307 * Originally Released Under LGPL - original licence link has changed is not relivant.
12310 * <script type="text/javascript">
12314 * @class Roo.data.ScriptTagProxy
12315 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12316 * other than the originating domain of the running page.<br><br>
12318 * <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
12319 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12321 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12322 * source code that is used as the source inside a <script> tag.<br><br>
12324 * In order for the browser to process the returned data, the server must wrap the data object
12325 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12326 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12327 * depending on whether the callback name was passed:
12330 boolean scriptTag = false;
12331 String cb = request.getParameter("callback");
12334 response.setContentType("text/javascript");
12336 response.setContentType("application/x-json");
12338 Writer out = response.getWriter();
12340 out.write(cb + "(");
12342 out.print(dataBlock.toJsonString());
12349 * @param {Object} config A configuration object.
12351 Roo.data.ScriptTagProxy = function(config){
12352 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12353 Roo.apply(this, config);
12354 this.head = document.getElementsByTagName("head")[0];
12357 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12359 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12361 * @cfg {String} url The URL from which to request the data object.
12364 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12368 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12369 * the server the name of the callback function set up by the load call to process the returned data object.
12370 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12371 * javascript output which calls this named function passing the data object as its only parameter.
12373 callbackParam : "callback",
12375 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12376 * name to the request.
12381 * Load data from the configured URL, read the data object into
12382 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12383 * process that block using the passed callback.
12384 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12385 * for the request to the remote server.
12386 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12387 * object into a block of Roo.data.Records.
12388 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12389 * The function must be passed <ul>
12390 * <li>The Record block object</li>
12391 * <li>The "arg" argument from the load function</li>
12392 * <li>A boolean success indicator</li>
12394 * @param {Object} scope The scope in which to call the callback
12395 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12397 load : function(params, reader, callback, scope, arg){
12398 if(this.fireEvent("beforeload", this, params) !== false){
12400 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12402 var url = this.url;
12403 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12405 url += "&_dc=" + (new Date().getTime());
12407 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12410 cb : "stcCallback"+transId,
12411 scriptId : "stcScript"+transId,
12415 callback : callback,
12421 window[trans.cb] = function(o){
12422 conn.handleResponse(o, trans);
12425 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12427 if(this.autoAbort !== false){
12431 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12433 var script = document.createElement("script");
12434 script.setAttribute("src", url);
12435 script.setAttribute("type", "text/javascript");
12436 script.setAttribute("id", trans.scriptId);
12437 this.head.appendChild(script);
12439 this.trans = trans;
12441 callback.call(scope||this, null, arg, false);
12446 isLoading : function(){
12447 return this.trans ? true : false;
12451 * Abort the current server request.
12453 abort : function(){
12454 if(this.isLoading()){
12455 this.destroyTrans(this.trans);
12460 destroyTrans : function(trans, isLoaded){
12461 this.head.removeChild(document.getElementById(trans.scriptId));
12462 clearTimeout(trans.timeoutId);
12464 window[trans.cb] = undefined;
12466 delete window[trans.cb];
12469 // if hasn't been loaded, wait for load to remove it to prevent script error
12470 window[trans.cb] = function(){
12471 window[trans.cb] = undefined;
12473 delete window[trans.cb];
12480 handleResponse : function(o, trans){
12481 this.trans = false;
12482 this.destroyTrans(trans, true);
12485 result = trans.reader.readRecords(o);
12487 this.fireEvent("loadexception", this, o, trans.arg, e);
12488 trans.callback.call(trans.scope||window, null, trans.arg, false);
12491 this.fireEvent("load", this, o, trans.arg);
12492 trans.callback.call(trans.scope||window, result, trans.arg, true);
12496 handleFailure : function(trans){
12497 this.trans = false;
12498 this.destroyTrans(trans, false);
12499 this.fireEvent("loadexception", this, null, trans.arg);
12500 trans.callback.call(trans.scope||window, null, trans.arg, false);
12504 * Ext JS Library 1.1.1
12505 * Copyright(c) 2006-2007, Ext JS, LLC.
12507 * Originally Released Under LGPL - original licence link has changed is not relivant.
12510 * <script type="text/javascript">
12514 * @class Roo.data.JsonReader
12515 * @extends Roo.data.DataReader
12516 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12517 * based on mappings in a provided Roo.data.Record constructor.
12519 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12520 * in the reply previously.
12525 var RecordDef = Roo.data.Record.create([
12526 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12527 {name: 'occupation'} // This field will use "occupation" as the mapping.
12529 var myReader = new Roo.data.JsonReader({
12530 totalProperty: "results", // The property which contains the total dataset size (optional)
12531 root: "rows", // The property which contains an Array of row objects
12532 id: "id" // The property within each row object that provides an ID for the record (optional)
12536 * This would consume a JSON file like this:
12538 { 'results': 2, 'rows': [
12539 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12540 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12543 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12544 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12545 * paged from the remote server.
12546 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12547 * @cfg {String} root name of the property which contains the Array of row objects.
12548 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12549 * @cfg {Array} fields Array of field definition objects
12551 * Create a new JsonReader
12552 * @param {Object} meta Metadata configuration options
12553 * @param {Object} recordType Either an Array of field definition objects,
12554 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12556 Roo.data.JsonReader = function(meta, recordType){
12559 // set some defaults:
12560 Roo.applyIf(meta, {
12561 totalProperty: 'total',
12562 successProperty : 'success',
12567 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12569 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12572 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12573 * Used by Store query builder to append _requestMeta to params.
12576 metaFromRemote : false,
12578 * This method is only used by a DataProxy which has retrieved data from a remote server.
12579 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12580 * @return {Object} data A data block which is used by an Roo.data.Store object as
12581 * a cache of Roo.data.Records.
12583 read : function(response){
12584 var json = response.responseText;
12586 var o = /* eval:var:o */ eval("("+json+")");
12588 throw {message: "JsonReader.read: Json object not found"};
12594 this.metaFromRemote = true;
12595 this.meta = o.metaData;
12596 this.recordType = Roo.data.Record.create(o.metaData.fields);
12597 this.onMetaChange(this.meta, this.recordType, o);
12599 return this.readRecords(o);
12602 // private function a store will implement
12603 onMetaChange : function(meta, recordType, o){
12610 simpleAccess: function(obj, subsc) {
12617 getJsonAccessor: function(){
12619 return function(expr) {
12621 return(re.test(expr))
12622 ? new Function("obj", "return obj." + expr)
12627 return Roo.emptyFn;
12632 * Create a data block containing Roo.data.Records from an XML document.
12633 * @param {Object} o An object which contains an Array of row objects in the property specified
12634 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12635 * which contains the total size of the dataset.
12636 * @return {Object} data A data block which is used by an Roo.data.Store object as
12637 * a cache of Roo.data.Records.
12639 readRecords : function(o){
12641 * After any data loads, the raw JSON data is available for further custom processing.
12645 var s = this.meta, Record = this.recordType,
12646 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12648 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12650 if(s.totalProperty) {
12651 this.getTotal = this.getJsonAccessor(s.totalProperty);
12653 if(s.successProperty) {
12654 this.getSuccess = this.getJsonAccessor(s.successProperty);
12656 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12658 var g = this.getJsonAccessor(s.id);
12659 this.getId = function(rec) {
12661 return (r === undefined || r === "") ? null : r;
12664 this.getId = function(){return null;};
12667 for(var jj = 0; jj < fl; jj++){
12669 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12670 this.ef[jj] = this.getJsonAccessor(map);
12674 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12675 if(s.totalProperty){
12676 var vt = parseInt(this.getTotal(o), 10);
12681 if(s.successProperty){
12682 var vs = this.getSuccess(o);
12683 if(vs === false || vs === 'false'){
12688 for(var i = 0; i < c; i++){
12691 var id = this.getId(n);
12692 for(var j = 0; j < fl; j++){
12694 var v = this.ef[j](n);
12696 Roo.log('missing convert for ' + f.name);
12700 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12702 var record = new Record(values, id);
12704 records[i] = record;
12710 totalRecords : totalRecords
12715 * Ext JS Library 1.1.1
12716 * Copyright(c) 2006-2007, Ext JS, LLC.
12718 * Originally Released Under LGPL - original licence link has changed is not relivant.
12721 * <script type="text/javascript">
12725 * @class Roo.data.ArrayReader
12726 * @extends Roo.data.DataReader
12727 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12728 * Each element of that Array represents a row of data fields. The
12729 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12730 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12734 var RecordDef = Roo.data.Record.create([
12735 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12736 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12738 var myReader = new Roo.data.ArrayReader({
12739 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12743 * This would consume an Array like this:
12745 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12747 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12749 * Create a new JsonReader
12750 * @param {Object} meta Metadata configuration options.
12751 * @param {Object} recordType Either an Array of field definition objects
12752 * as specified to {@link Roo.data.Record#create},
12753 * or an {@link Roo.data.Record} object
12754 * created using {@link Roo.data.Record#create}.
12756 Roo.data.ArrayReader = function(meta, recordType){
12757 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12760 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12762 * Create a data block containing Roo.data.Records from an XML document.
12763 * @param {Object} o An Array of row objects which represents the dataset.
12764 * @return {Object} data A data block which is used by an Roo.data.Store object as
12765 * a cache of Roo.data.Records.
12767 readRecords : function(o){
12768 var sid = this.meta ? this.meta.id : null;
12769 var recordType = this.recordType, fields = recordType.prototype.fields;
12772 for(var i = 0; i < root.length; i++){
12775 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12776 for(var j = 0, jlen = fields.length; j < jlen; j++){
12777 var f = fields.items[j];
12778 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12779 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12781 values[f.name] = v;
12783 var record = new recordType(values, id);
12785 records[records.length] = record;
12789 totalRecords : records.length
12798 * @class Roo.bootstrap.ComboBox
12799 * @extends Roo.bootstrap.TriggerField
12800 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12801 * @cfg {Boolean} append (true|false) default false
12802 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12803 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12804 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12805 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12806 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12807 * @cfg {Boolean} animate default true
12808 * @cfg {Boolean} emptyResultText only for touch device
12809 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12810 * @cfg {String} emptyTitle default ''
12812 * Create a new ComboBox.
12813 * @param {Object} config Configuration options
12815 Roo.bootstrap.ComboBox = function(config){
12816 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12820 * Fires when the dropdown list is expanded
12821 * @param {Roo.bootstrap.ComboBox} combo This combo box
12826 * Fires when the dropdown list is collapsed
12827 * @param {Roo.bootstrap.ComboBox} combo This combo box
12831 * @event beforeselect
12832 * Fires before a list item is selected. Return false to cancel the selection.
12833 * @param {Roo.bootstrap.ComboBox} combo This combo box
12834 * @param {Roo.data.Record} record The data record returned from the underlying store
12835 * @param {Number} index The index of the selected item in the dropdown list
12837 'beforeselect' : true,
12840 * Fires when a list item is selected
12841 * @param {Roo.bootstrap.ComboBox} combo This combo box
12842 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12843 * @param {Number} index The index of the selected item in the dropdown list
12847 * @event beforequery
12848 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12849 * The event object passed has these properties:
12850 * @param {Roo.bootstrap.ComboBox} combo This combo box
12851 * @param {String} query The query
12852 * @param {Boolean} forceAll true to force "all" query
12853 * @param {Boolean} cancel true to cancel the query
12854 * @param {Object} e The query event object
12856 'beforequery': true,
12859 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12860 * @param {Roo.bootstrap.ComboBox} combo This combo box
12865 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12866 * @param {Roo.bootstrap.ComboBox} combo This combo box
12867 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12872 * Fires when the remove value from the combobox array
12873 * @param {Roo.bootstrap.ComboBox} combo This combo box
12877 * @event afterremove
12878 * Fires when the remove value from the combobox array
12879 * @param {Roo.bootstrap.ComboBox} combo This combo box
12881 'afterremove' : true,
12883 * @event specialfilter
12884 * Fires when specialfilter
12885 * @param {Roo.bootstrap.ComboBox} combo This combo box
12887 'specialfilter' : true,
12890 * Fires when tick the element
12891 * @param {Roo.bootstrap.ComboBox} combo This combo box
12895 * @event touchviewdisplay
12896 * Fires when touch view require special display (default is using displayField)
12897 * @param {Roo.bootstrap.ComboBox} combo This combo box
12898 * @param {Object} cfg set html .
12900 'touchviewdisplay' : true
12905 this.tickItems = [];
12907 this.selectedIndex = -1;
12908 if(this.mode == 'local'){
12909 if(config.queryDelay === undefined){
12910 this.queryDelay = 10;
12912 if(config.minChars === undefined){
12918 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12921 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12922 * rendering into an Roo.Editor, defaults to false)
12925 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12926 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12929 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12932 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12933 * the dropdown list (defaults to undefined, with no header element)
12937 * @cfg {String/Roo.Template} tpl The template to use to render the output
12941 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12943 listWidth: undefined,
12945 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12946 * mode = 'remote' or 'text' if mode = 'local')
12948 displayField: undefined,
12951 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12952 * mode = 'remote' or 'value' if mode = 'local').
12953 * Note: use of a valueField requires the user make a selection
12954 * in order for a value to be mapped.
12956 valueField: undefined,
12958 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12963 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12964 * field's data value (defaults to the underlying DOM element's name)
12966 hiddenName: undefined,
12968 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12972 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12974 selectedClass: 'active',
12977 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12981 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12982 * anchor positions (defaults to 'tl-bl')
12984 listAlign: 'tl-bl?',
12986 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12990 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12991 * query specified by the allQuery config option (defaults to 'query')
12993 triggerAction: 'query',
12995 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12996 * (defaults to 4, does not apply if editable = false)
13000 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13001 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13005 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13006 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13010 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13011 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13015 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13016 * when editable = true (defaults to false)
13018 selectOnFocus:false,
13020 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13022 queryParam: 'query',
13024 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13025 * when mode = 'remote' (defaults to 'Loading...')
13027 loadingText: 'Loading...',
13029 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13033 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13037 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13038 * traditional select (defaults to true)
13042 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13046 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13050 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13051 * listWidth has a higher value)
13055 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13056 * allow the user to set arbitrary text into the field (defaults to false)
13058 forceSelection:false,
13060 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13061 * if typeAhead = true (defaults to 250)
13063 typeAheadDelay : 250,
13065 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13066 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13068 valueNotFoundText : undefined,
13070 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13072 blockFocus : false,
13075 * @cfg {Boolean} disableClear Disable showing of clear button.
13077 disableClear : false,
13079 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13081 alwaysQuery : false,
13084 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13089 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13091 invalidClass : "has-warning",
13094 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13096 validClass : "has-success",
13099 * @cfg {Boolean} specialFilter (true|false) special filter default false
13101 specialFilter : false,
13104 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13106 mobileTouchView : true,
13109 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13111 useNativeIOS : false,
13114 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13116 mobile_restrict_height : false,
13118 ios_options : false,
13130 btnPosition : 'right',
13131 triggerList : true,
13132 showToggleBtn : true,
13134 emptyResultText: 'Empty',
13135 triggerText : 'Select',
13138 // element that contains real text value.. (when hidden is used..)
13140 getAutoCreate : function()
13145 * Render classic select for iso
13148 if(Roo.isIOS && this.useNativeIOS){
13149 cfg = this.getAutoCreateNativeIOS();
13157 if(Roo.isTouch && this.mobileTouchView){
13158 cfg = this.getAutoCreateTouchView();
13165 if(!this.tickable){
13166 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13171 * ComboBox with tickable selections
13174 var align = this.labelAlign || this.parentLabelAlign();
13177 cls : 'form-group roo-combobox-tickable' //input-group
13180 var btn_text_select = '';
13181 var btn_text_done = '';
13182 var btn_text_cancel = '';
13184 if (this.btn_text_show) {
13185 btn_text_select = 'Select';
13186 btn_text_done = 'Done';
13187 btn_text_cancel = 'Cancel';
13192 cls : 'tickable-buttons',
13197 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13198 //html : this.triggerText
13199 html: btn_text_select
13205 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13207 html: btn_text_done
13213 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13215 html: btn_text_cancel
13221 buttons.cn.unshift({
13223 cls: 'roo-select2-search-field-input'
13229 Roo.each(buttons.cn, function(c){
13231 c.cls += ' btn-' + _this.size;
13234 if (_this.disabled) {
13245 cls: 'form-hidden-field'
13249 cls: 'roo-select2-choices',
13253 cls: 'roo-select2-search-field',
13264 cls: 'roo-select2-container input-group roo-select2-container-multi',
13269 // cls: 'typeahead typeahead-long dropdown-menu',
13270 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13275 if(this.hasFeedback && !this.allowBlank){
13279 cls: 'glyphicon form-control-feedback'
13282 combobox.cn.push(feedback);
13286 if (align ==='left' && this.fieldLabel.length) {
13288 cfg.cls += ' roo-form-group-label-left';
13293 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13294 tooltip : 'This field is required'
13299 cls : 'control-label',
13300 html : this.fieldLabel
13312 var labelCfg = cfg.cn[1];
13313 var contentCfg = cfg.cn[2];
13316 if(this.indicatorpos == 'right'){
13322 cls : 'control-label',
13326 html : this.fieldLabel
13330 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13331 tooltip : 'This field is required'
13346 labelCfg = cfg.cn[0];
13347 contentCfg = cfg.cn[1];
13351 if(this.labelWidth > 12){
13352 labelCfg.style = "width: " + this.labelWidth + 'px';
13355 if(this.labelWidth < 13 && this.labelmd == 0){
13356 this.labelmd = this.labelWidth;
13359 if(this.labellg > 0){
13360 labelCfg.cls += ' col-lg-' + this.labellg;
13361 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13364 if(this.labelmd > 0){
13365 labelCfg.cls += ' col-md-' + this.labelmd;
13366 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13369 if(this.labelsm > 0){
13370 labelCfg.cls += ' col-sm-' + this.labelsm;
13371 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13374 if(this.labelxs > 0){
13375 labelCfg.cls += ' col-xs-' + this.labelxs;
13376 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13380 } else if ( this.fieldLabel.length) {
13381 // Roo.log(" label");
13385 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13386 tooltip : 'This field is required'
13390 //cls : 'input-group-addon',
13391 html : this.fieldLabel
13396 if(this.indicatorpos == 'right'){
13400 //cls : 'input-group-addon',
13401 html : this.fieldLabel
13405 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13406 tooltip : 'This field is required'
13415 // Roo.log(" no label && no align");
13422 ['xs','sm','md','lg'].map(function(size){
13423 if (settings[size]) {
13424 cfg.cls += ' col-' + size + '-' + settings[size];
13432 _initEventsCalled : false,
13435 initEvents: function()
13437 if (this._initEventsCalled) { // as we call render... prevent looping...
13440 this._initEventsCalled = true;
13443 throw "can not find store for combo";
13446 this.indicator = this.indicatorEl();
13448 this.store = Roo.factory(this.store, Roo.data);
13449 this.store.parent = this;
13451 // if we are building from html. then this element is so complex, that we can not really
13452 // use the rendered HTML.
13453 // so we have to trash and replace the previous code.
13454 if (Roo.XComponent.build_from_html) {
13455 // remove this element....
13456 var e = this.el.dom, k=0;
13457 while (e ) { e = e.previousSibling; ++k;}
13462 this.rendered = false;
13464 this.render(this.parent().getChildContainer(true), k);
13467 if(Roo.isIOS && this.useNativeIOS){
13468 this.initIOSView();
13476 if(Roo.isTouch && this.mobileTouchView){
13477 this.initTouchView();
13482 this.initTickableEvents();
13486 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13488 if(this.hiddenName){
13490 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13492 this.hiddenField.dom.value =
13493 this.hiddenValue !== undefined ? this.hiddenValue :
13494 this.value !== undefined ? this.value : '';
13496 // prevent input submission
13497 this.el.dom.removeAttribute('name');
13498 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13503 // this.el.dom.setAttribute('autocomplete', 'off');
13506 var cls = 'x-combo-list';
13508 //this.list = new Roo.Layer({
13509 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13515 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13516 _this.list.setWidth(lw);
13519 this.list.on('mouseover', this.onViewOver, this);
13520 this.list.on('mousemove', this.onViewMove, this);
13521 this.list.on('scroll', this.onViewScroll, this);
13524 this.list.swallowEvent('mousewheel');
13525 this.assetHeight = 0;
13528 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13529 this.assetHeight += this.header.getHeight();
13532 this.innerList = this.list.createChild({cls:cls+'-inner'});
13533 this.innerList.on('mouseover', this.onViewOver, this);
13534 this.innerList.on('mousemove', this.onViewMove, this);
13535 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13537 if(this.allowBlank && !this.pageSize && !this.disableClear){
13538 this.footer = this.list.createChild({cls:cls+'-ft'});
13539 this.pageTb = new Roo.Toolbar(this.footer);
13543 this.footer = this.list.createChild({cls:cls+'-ft'});
13544 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13545 {pageSize: this.pageSize});
13549 if (this.pageTb && this.allowBlank && !this.disableClear) {
13551 this.pageTb.add(new Roo.Toolbar.Fill(), {
13552 cls: 'x-btn-icon x-btn-clear',
13554 handler: function()
13557 _this.clearValue();
13558 _this.onSelect(false, -1);
13563 this.assetHeight += this.footer.getHeight();
13568 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13571 this.view = new Roo.View(this.list, this.tpl, {
13572 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13574 //this.view.wrapEl.setDisplayed(false);
13575 this.view.on('click', this.onViewClick, this);
13578 this.store.on('beforeload', this.onBeforeLoad, this);
13579 this.store.on('load', this.onLoad, this);
13580 this.store.on('loadexception', this.onLoadException, this);
13582 if(this.resizable){
13583 this.resizer = new Roo.Resizable(this.list, {
13584 pinned:true, handles:'se'
13586 this.resizer.on('resize', function(r, w, h){
13587 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13588 this.listWidth = w;
13589 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13590 this.restrictHeight();
13592 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13595 if(!this.editable){
13596 this.editable = true;
13597 this.setEditable(false);
13602 if (typeof(this.events.add.listeners) != 'undefined') {
13604 this.addicon = this.wrap.createChild(
13605 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13607 this.addicon.on('click', function(e) {
13608 this.fireEvent('add', this);
13611 if (typeof(this.events.edit.listeners) != 'undefined') {
13613 this.editicon = this.wrap.createChild(
13614 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13615 if (this.addicon) {
13616 this.editicon.setStyle('margin-left', '40px');
13618 this.editicon.on('click', function(e) {
13620 // we fire even if inothing is selected..
13621 this.fireEvent('edit', this, this.lastData );
13627 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13628 "up" : function(e){
13629 this.inKeyMode = true;
13633 "down" : function(e){
13634 if(!this.isExpanded()){
13635 this.onTriggerClick();
13637 this.inKeyMode = true;
13642 "enter" : function(e){
13643 // this.onViewClick();
13647 if(this.fireEvent("specialkey", this, e)){
13648 this.onViewClick(false);
13654 "esc" : function(e){
13658 "tab" : function(e){
13661 if(this.fireEvent("specialkey", this, e)){
13662 this.onViewClick(false);
13670 doRelay : function(foo, bar, hname){
13671 if(hname == 'down' || this.scope.isExpanded()){
13672 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13681 this.queryDelay = Math.max(this.queryDelay || 10,
13682 this.mode == 'local' ? 10 : 250);
13685 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13687 if(this.typeAhead){
13688 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13690 if(this.editable !== false){
13691 this.inputEl().on("keyup", this.onKeyUp, this);
13693 if(this.forceSelection){
13694 this.inputEl().on('blur', this.doForce, this);
13698 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13699 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13703 initTickableEvents: function()
13707 if(this.hiddenName){
13709 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13711 this.hiddenField.dom.value =
13712 this.hiddenValue !== undefined ? this.hiddenValue :
13713 this.value !== undefined ? this.value : '';
13715 // prevent input submission
13716 this.el.dom.removeAttribute('name');
13717 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13722 // this.list = this.el.select('ul.dropdown-menu',true).first();
13724 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13725 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13726 if(this.triggerList){
13727 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13730 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13731 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13733 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13734 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13736 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13737 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13739 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13740 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13741 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13744 this.cancelBtn.hide();
13749 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13750 _this.list.setWidth(lw);
13753 this.list.on('mouseover', this.onViewOver, this);
13754 this.list.on('mousemove', this.onViewMove, this);
13756 this.list.on('scroll', this.onViewScroll, this);
13759 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13760 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13763 this.view = new Roo.View(this.list, this.tpl, {
13768 selectedClass: this.selectedClass
13771 //this.view.wrapEl.setDisplayed(false);
13772 this.view.on('click', this.onViewClick, this);
13776 this.store.on('beforeload', this.onBeforeLoad, this);
13777 this.store.on('load', this.onLoad, this);
13778 this.store.on('loadexception', this.onLoadException, this);
13781 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13782 "up" : function(e){
13783 this.inKeyMode = true;
13787 "down" : function(e){
13788 this.inKeyMode = true;
13792 "enter" : function(e){
13793 if(this.fireEvent("specialkey", this, e)){
13794 this.onViewClick(false);
13800 "esc" : function(e){
13801 this.onTickableFooterButtonClick(e, false, false);
13804 "tab" : function(e){
13805 this.fireEvent("specialkey", this, e);
13807 this.onTickableFooterButtonClick(e, false, false);
13814 doRelay : function(e, fn, key){
13815 if(this.scope.isExpanded()){
13816 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13825 this.queryDelay = Math.max(this.queryDelay || 10,
13826 this.mode == 'local' ? 10 : 250);
13829 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13831 if(this.typeAhead){
13832 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13835 if(this.editable !== false){
13836 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13839 this.indicator = this.indicatorEl();
13841 if(this.indicator){
13842 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13843 this.indicator.hide();
13848 onDestroy : function(){
13850 this.view.setStore(null);
13851 this.view.el.removeAllListeners();
13852 this.view.el.remove();
13853 this.view.purgeListeners();
13856 this.list.dom.innerHTML = '';
13860 this.store.un('beforeload', this.onBeforeLoad, this);
13861 this.store.un('load', this.onLoad, this);
13862 this.store.un('loadexception', this.onLoadException, this);
13864 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13868 fireKey : function(e){
13869 if(e.isNavKeyPress() && !this.list.isVisible()){
13870 this.fireEvent("specialkey", this, e);
13875 onResize: function(w, h){
13876 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13878 // if(typeof w != 'number'){
13879 // // we do not handle it!?!?
13882 // var tw = this.trigger.getWidth();
13883 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13884 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13886 // this.inputEl().setWidth( this.adjustWidth('input', x));
13888 // //this.trigger.setStyle('left', x+'px');
13890 // if(this.list && this.listWidth === undefined){
13891 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13892 // this.list.setWidth(lw);
13893 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13901 * Allow or prevent the user from directly editing the field text. If false is passed,
13902 * the user will only be able to select from the items defined in the dropdown list. This method
13903 * is the runtime equivalent of setting the 'editable' config option at config time.
13904 * @param {Boolean} value True to allow the user to directly edit the field text
13906 setEditable : function(value){
13907 if(value == this.editable){
13910 this.editable = value;
13912 this.inputEl().dom.setAttribute('readOnly', true);
13913 this.inputEl().on('mousedown', this.onTriggerClick, this);
13914 this.inputEl().addClass('x-combo-noedit');
13916 this.inputEl().dom.setAttribute('readOnly', false);
13917 this.inputEl().un('mousedown', this.onTriggerClick, this);
13918 this.inputEl().removeClass('x-combo-noedit');
13924 onBeforeLoad : function(combo,opts){
13925 if(!this.hasFocus){
13929 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13931 this.restrictHeight();
13932 this.selectedIndex = -1;
13936 onLoad : function(){
13938 this.hasQuery = false;
13940 if(!this.hasFocus){
13944 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13945 this.loading.hide();
13948 if(this.store.getCount() > 0){
13951 this.restrictHeight();
13952 if(this.lastQuery == this.allQuery){
13953 if(this.editable && !this.tickable){
13954 this.inputEl().dom.select();
13958 !this.selectByValue(this.value, true) &&
13961 !this.store.lastOptions ||
13962 typeof(this.store.lastOptions.add) == 'undefined' ||
13963 this.store.lastOptions.add != true
13966 this.select(0, true);
13969 if(this.autoFocus){
13972 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13973 this.taTask.delay(this.typeAheadDelay);
13977 this.onEmptyResults();
13983 onLoadException : function()
13985 this.hasQuery = false;
13987 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13988 this.loading.hide();
13991 if(this.tickable && this.editable){
13996 // only causes errors at present
13997 //Roo.log(this.store.reader.jsonData);
13998 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14000 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14006 onTypeAhead : function(){
14007 if(this.store.getCount() > 0){
14008 var r = this.store.getAt(0);
14009 var newValue = r.data[this.displayField];
14010 var len = newValue.length;
14011 var selStart = this.getRawValue().length;
14013 if(selStart != len){
14014 this.setRawValue(newValue);
14015 this.selectText(selStart, newValue.length);
14021 onSelect : function(record, index){
14023 if(this.fireEvent('beforeselect', this, record, index) !== false){
14025 this.setFromData(index > -1 ? record.data : false);
14028 this.fireEvent('select', this, record, index);
14033 * Returns the currently selected field value or empty string if no value is set.
14034 * @return {String} value The selected value
14036 getValue : function()
14038 if(Roo.isIOS && this.useNativeIOS){
14039 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14043 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14046 if(this.valueField){
14047 return typeof this.value != 'undefined' ? this.value : '';
14049 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14053 getRawValue : function()
14055 if(Roo.isIOS && this.useNativeIOS){
14056 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14059 var v = this.inputEl().getValue();
14065 * Clears any text/value currently set in the field
14067 clearValue : function(){
14069 if(this.hiddenField){
14070 this.hiddenField.dom.value = '';
14073 this.setRawValue('');
14074 this.lastSelectionText = '';
14075 this.lastData = false;
14077 var close = this.closeTriggerEl();
14088 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14089 * will be displayed in the field. If the value does not match the data value of an existing item,
14090 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14091 * Otherwise the field will be blank (although the value will still be set).
14092 * @param {String} value The value to match
14094 setValue : function(v)
14096 if(Roo.isIOS && this.useNativeIOS){
14097 this.setIOSValue(v);
14107 if(this.valueField){
14108 var r = this.findRecord(this.valueField, v);
14110 text = r.data[this.displayField];
14111 }else if(this.valueNotFoundText !== undefined){
14112 text = this.valueNotFoundText;
14115 this.lastSelectionText = text;
14116 if(this.hiddenField){
14117 this.hiddenField.dom.value = v;
14119 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14122 var close = this.closeTriggerEl();
14125 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14131 * @property {Object} the last set data for the element
14136 * Sets the value of the field based on a object which is related to the record format for the store.
14137 * @param {Object} value the value to set as. or false on reset?
14139 setFromData : function(o){
14146 var dv = ''; // display value
14147 var vv = ''; // value value..
14149 if (this.displayField) {
14150 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14152 // this is an error condition!!!
14153 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14156 if(this.valueField){
14157 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14160 var close = this.closeTriggerEl();
14163 if(dv.length || vv * 1 > 0){
14165 this.blockFocus=true;
14171 if(this.hiddenField){
14172 this.hiddenField.dom.value = vv;
14174 this.lastSelectionText = dv;
14175 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14179 // no hidden field.. - we store the value in 'value', but still display
14180 // display field!!!!
14181 this.lastSelectionText = dv;
14182 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14189 reset : function(){
14190 // overridden so that last data is reset..
14197 this.setValue(this.originalValue);
14198 //this.clearInvalid();
14199 this.lastData = false;
14201 this.view.clearSelections();
14207 findRecord : function(prop, value){
14209 if(this.store.getCount() > 0){
14210 this.store.each(function(r){
14211 if(r.data[prop] == value){
14221 getName: function()
14223 // returns hidden if it's set..
14224 if (!this.rendered) {return ''};
14225 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14229 onViewMove : function(e, t){
14230 this.inKeyMode = false;
14234 onViewOver : function(e, t){
14235 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14238 var item = this.view.findItemFromChild(t);
14241 var index = this.view.indexOf(item);
14242 this.select(index, false);
14247 onViewClick : function(view, doFocus, el, e)
14249 var index = this.view.getSelectedIndexes()[0];
14251 var r = this.store.getAt(index);
14255 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14262 Roo.each(this.tickItems, function(v,k){
14264 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14266 _this.tickItems.splice(k, 1);
14268 if(typeof(e) == 'undefined' && view == false){
14269 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14281 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14282 this.tickItems.push(r.data);
14285 if(typeof(e) == 'undefined' && view == false){
14286 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14293 this.onSelect(r, index);
14295 if(doFocus !== false && !this.blockFocus){
14296 this.inputEl().focus();
14301 restrictHeight : function(){
14302 //this.innerList.dom.style.height = '';
14303 //var inner = this.innerList.dom;
14304 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14305 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14306 //this.list.beginUpdate();
14307 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14308 this.list.alignTo(this.inputEl(), this.listAlign);
14309 this.list.alignTo(this.inputEl(), this.listAlign);
14310 //this.list.endUpdate();
14314 onEmptyResults : function(){
14316 if(this.tickable && this.editable){
14317 this.hasFocus = false;
14318 this.restrictHeight();
14326 * Returns true if the dropdown list is expanded, else false.
14328 isExpanded : function(){
14329 return this.list.isVisible();
14333 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14334 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14335 * @param {String} value The data value of the item to select
14336 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14337 * selected item if it is not currently in view (defaults to true)
14338 * @return {Boolean} True if the value matched an item in the list, else false
14340 selectByValue : function(v, scrollIntoView){
14341 if(v !== undefined && v !== null){
14342 var r = this.findRecord(this.valueField || this.displayField, v);
14344 this.select(this.store.indexOf(r), scrollIntoView);
14352 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14353 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14354 * @param {Number} index The zero-based index of the list item to select
14355 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14356 * selected item if it is not currently in view (defaults to true)
14358 select : function(index, scrollIntoView){
14359 this.selectedIndex = index;
14360 this.view.select(index);
14361 if(scrollIntoView !== false){
14362 var el = this.view.getNode(index);
14364 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14367 this.list.scrollChildIntoView(el, false);
14373 selectNext : function(){
14374 var ct = this.store.getCount();
14376 if(this.selectedIndex == -1){
14378 }else if(this.selectedIndex < ct-1){
14379 this.select(this.selectedIndex+1);
14385 selectPrev : function(){
14386 var ct = this.store.getCount();
14388 if(this.selectedIndex == -1){
14390 }else if(this.selectedIndex != 0){
14391 this.select(this.selectedIndex-1);
14397 onKeyUp : function(e){
14398 if(this.editable !== false && !e.isSpecialKey()){
14399 this.lastKey = e.getKey();
14400 this.dqTask.delay(this.queryDelay);
14405 validateBlur : function(){
14406 return !this.list || !this.list.isVisible();
14410 initQuery : function(){
14412 var v = this.getRawValue();
14414 if(this.tickable && this.editable){
14415 v = this.tickableInputEl().getValue();
14422 doForce : function(){
14423 if(this.inputEl().dom.value.length > 0){
14424 this.inputEl().dom.value =
14425 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14431 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14432 * query allowing the query action to be canceled if needed.
14433 * @param {String} query The SQL query to execute
14434 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14435 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14436 * saved in the current store (defaults to false)
14438 doQuery : function(q, forceAll){
14440 if(q === undefined || q === null){
14445 forceAll: forceAll,
14449 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14454 forceAll = qe.forceAll;
14455 if(forceAll === true || (q.length >= this.minChars)){
14457 this.hasQuery = true;
14459 if(this.lastQuery != q || this.alwaysQuery){
14460 this.lastQuery = q;
14461 if(this.mode == 'local'){
14462 this.selectedIndex = -1;
14464 this.store.clearFilter();
14467 if(this.specialFilter){
14468 this.fireEvent('specialfilter', this);
14473 this.store.filter(this.displayField, q);
14476 this.store.fireEvent("datachanged", this.store);
14483 this.store.baseParams[this.queryParam] = q;
14485 var options = {params : this.getParams(q)};
14488 options.add = true;
14489 options.params.start = this.page * this.pageSize;
14492 this.store.load(options);
14495 * this code will make the page width larger, at the beginning, the list not align correctly,
14496 * we should expand the list on onLoad
14497 * so command out it
14502 this.selectedIndex = -1;
14507 this.loadNext = false;
14511 getParams : function(q){
14513 //p[this.queryParam] = q;
14517 p.limit = this.pageSize;
14523 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14525 collapse : function(){
14526 if(!this.isExpanded()){
14532 this.hasFocus = false;
14536 this.cancelBtn.hide();
14537 this.trigger.show();
14540 this.tickableInputEl().dom.value = '';
14541 this.tickableInputEl().blur();
14546 Roo.get(document).un('mousedown', this.collapseIf, this);
14547 Roo.get(document).un('mousewheel', this.collapseIf, this);
14548 if (!this.editable) {
14549 Roo.get(document).un('keydown', this.listKeyPress, this);
14551 this.fireEvent('collapse', this);
14557 collapseIf : function(e){
14558 var in_combo = e.within(this.el);
14559 var in_list = e.within(this.list);
14560 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14562 if (in_combo || in_list || is_list) {
14563 //e.stopPropagation();
14568 this.onTickableFooterButtonClick(e, false, false);
14576 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14578 expand : function(){
14580 if(this.isExpanded() || !this.hasFocus){
14584 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14585 this.list.setWidth(lw);
14591 this.restrictHeight();
14595 this.tickItems = Roo.apply([], this.item);
14598 this.cancelBtn.show();
14599 this.trigger.hide();
14602 this.tickableInputEl().focus();
14607 Roo.get(document).on('mousedown', this.collapseIf, this);
14608 Roo.get(document).on('mousewheel', this.collapseIf, this);
14609 if (!this.editable) {
14610 Roo.get(document).on('keydown', this.listKeyPress, this);
14613 this.fireEvent('expand', this);
14617 // Implements the default empty TriggerField.onTriggerClick function
14618 onTriggerClick : function(e)
14620 Roo.log('trigger click');
14622 if(this.disabled || !this.triggerList){
14627 this.loadNext = false;
14629 if(this.isExpanded()){
14631 if (!this.blockFocus) {
14632 this.inputEl().focus();
14636 this.hasFocus = true;
14637 if(this.triggerAction == 'all') {
14638 this.doQuery(this.allQuery, true);
14640 this.doQuery(this.getRawValue());
14642 if (!this.blockFocus) {
14643 this.inputEl().focus();
14648 onTickableTriggerClick : function(e)
14655 this.loadNext = false;
14656 this.hasFocus = true;
14658 if(this.triggerAction == 'all') {
14659 this.doQuery(this.allQuery, true);
14661 this.doQuery(this.getRawValue());
14665 onSearchFieldClick : function(e)
14667 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14668 this.onTickableFooterButtonClick(e, false, false);
14672 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14677 this.loadNext = false;
14678 this.hasFocus = true;
14680 if(this.triggerAction == 'all') {
14681 this.doQuery(this.allQuery, true);
14683 this.doQuery(this.getRawValue());
14687 listKeyPress : function(e)
14689 //Roo.log('listkeypress');
14690 // scroll to first matching element based on key pres..
14691 if (e.isSpecialKey()) {
14694 var k = String.fromCharCode(e.getKey()).toUpperCase();
14697 var csel = this.view.getSelectedNodes();
14698 var cselitem = false;
14700 var ix = this.view.indexOf(csel[0]);
14701 cselitem = this.store.getAt(ix);
14702 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14708 this.store.each(function(v) {
14710 // start at existing selection.
14711 if (cselitem.id == v.id) {
14717 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14718 match = this.store.indexOf(v);
14724 if (match === false) {
14725 return true; // no more action?
14728 this.view.select(match);
14729 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14730 sn.scrollIntoView(sn.dom.parentNode, false);
14733 onViewScroll : function(e, t){
14735 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){
14739 this.hasQuery = true;
14741 this.loading = this.list.select('.loading', true).first();
14743 if(this.loading === null){
14744 this.list.createChild({
14746 cls: 'loading roo-select2-more-results roo-select2-active',
14747 html: 'Loading more results...'
14750 this.loading = this.list.select('.loading', true).first();
14752 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14754 this.loading.hide();
14757 this.loading.show();
14762 this.loadNext = true;
14764 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14769 addItem : function(o)
14771 var dv = ''; // display value
14773 if (this.displayField) {
14774 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14776 // this is an error condition!!!
14777 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14784 var choice = this.choices.createChild({
14786 cls: 'roo-select2-search-choice',
14795 cls: 'roo-select2-search-choice-close fa fa-times',
14800 }, this.searchField);
14802 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14804 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14812 this.inputEl().dom.value = '';
14817 onRemoveItem : function(e, _self, o)
14819 e.preventDefault();
14821 this.lastItem = Roo.apply([], this.item);
14823 var index = this.item.indexOf(o.data) * 1;
14826 Roo.log('not this item?!');
14830 this.item.splice(index, 1);
14835 this.fireEvent('remove', this, e);
14841 syncValue : function()
14843 if(!this.item.length){
14850 Roo.each(this.item, function(i){
14851 if(_this.valueField){
14852 value.push(i[_this.valueField]);
14859 this.value = value.join(',');
14861 if(this.hiddenField){
14862 this.hiddenField.dom.value = this.value;
14865 this.store.fireEvent("datachanged", this.store);
14870 clearItem : function()
14872 if(!this.multiple){
14878 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14886 if(this.tickable && !Roo.isTouch){
14887 this.view.refresh();
14891 inputEl: function ()
14893 if(Roo.isIOS && this.useNativeIOS){
14894 return this.el.select('select.roo-ios-select', true).first();
14897 if(Roo.isTouch && this.mobileTouchView){
14898 return this.el.select('input.form-control',true).first();
14902 return this.searchField;
14905 return this.el.select('input.form-control',true).first();
14908 onTickableFooterButtonClick : function(e, btn, el)
14910 e.preventDefault();
14912 this.lastItem = Roo.apply([], this.item);
14914 if(btn && btn.name == 'cancel'){
14915 this.tickItems = Roo.apply([], this.item);
14924 Roo.each(this.tickItems, function(o){
14932 validate : function()
14934 if(this.getVisibilityEl().hasClass('hidden')){
14938 var v = this.getRawValue();
14941 v = this.getValue();
14944 if(this.disabled || this.allowBlank || v.length){
14949 this.markInvalid();
14953 tickableInputEl : function()
14955 if(!this.tickable || !this.editable){
14956 return this.inputEl();
14959 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14963 getAutoCreateTouchView : function()
14968 cls: 'form-group' //input-group
14974 type : this.inputType,
14975 cls : 'form-control x-combo-noedit',
14976 autocomplete: 'new-password',
14977 placeholder : this.placeholder || '',
14982 input.name = this.name;
14986 input.cls += ' input-' + this.size;
14989 if (this.disabled) {
14990 input.disabled = true;
15001 inputblock.cls += ' input-group';
15003 inputblock.cn.unshift({
15005 cls : 'input-group-addon',
15010 if(this.removable && !this.multiple){
15011 inputblock.cls += ' roo-removable';
15013 inputblock.cn.push({
15016 cls : 'roo-combo-removable-btn close'
15020 if(this.hasFeedback && !this.allowBlank){
15022 inputblock.cls += ' has-feedback';
15024 inputblock.cn.push({
15026 cls: 'glyphicon form-control-feedback'
15033 inputblock.cls += (this.before) ? '' : ' input-group';
15035 inputblock.cn.push({
15037 cls : 'input-group-addon',
15048 cls: 'form-hidden-field'
15062 cls: 'form-hidden-field'
15066 cls: 'roo-select2-choices',
15070 cls: 'roo-select2-search-field',
15083 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15089 if(!this.multiple && this.showToggleBtn){
15096 if (this.caret != false) {
15099 cls: 'fa fa-' + this.caret
15106 cls : 'input-group-addon btn dropdown-toggle',
15111 cls: 'combobox-clear',
15125 combobox.cls += ' roo-select2-container-multi';
15128 var align = this.labelAlign || this.parentLabelAlign();
15130 if (align ==='left' && this.fieldLabel.length) {
15135 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15136 tooltip : 'This field is required'
15140 cls : 'control-label',
15141 html : this.fieldLabel
15152 var labelCfg = cfg.cn[1];
15153 var contentCfg = cfg.cn[2];
15156 if(this.indicatorpos == 'right'){
15161 cls : 'control-label',
15165 html : this.fieldLabel
15169 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15170 tooltip : 'This field is required'
15183 labelCfg = cfg.cn[0];
15184 contentCfg = cfg.cn[1];
15189 if(this.labelWidth > 12){
15190 labelCfg.style = "width: " + this.labelWidth + 'px';
15193 if(this.labelWidth < 13 && this.labelmd == 0){
15194 this.labelmd = this.labelWidth;
15197 if(this.labellg > 0){
15198 labelCfg.cls += ' col-lg-' + this.labellg;
15199 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15202 if(this.labelmd > 0){
15203 labelCfg.cls += ' col-md-' + this.labelmd;
15204 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15207 if(this.labelsm > 0){
15208 labelCfg.cls += ' col-sm-' + this.labelsm;
15209 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15212 if(this.labelxs > 0){
15213 labelCfg.cls += ' col-xs-' + this.labelxs;
15214 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15218 } else if ( this.fieldLabel.length) {
15222 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15223 tooltip : 'This field is required'
15227 cls : 'control-label',
15228 html : this.fieldLabel
15239 if(this.indicatorpos == 'right'){
15243 cls : 'control-label',
15244 html : this.fieldLabel,
15248 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15249 tooltip : 'This field is required'
15266 var settings = this;
15268 ['xs','sm','md','lg'].map(function(size){
15269 if (settings[size]) {
15270 cfg.cls += ' col-' + size + '-' + settings[size];
15277 initTouchView : function()
15279 this.renderTouchView();
15281 this.touchViewEl.on('scroll', function(){
15282 this.el.dom.scrollTop = 0;
15285 this.originalValue = this.getValue();
15287 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15289 this.inputEl().on("click", this.showTouchView, this);
15290 if (this.triggerEl) {
15291 this.triggerEl.on("click", this.showTouchView, this);
15295 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15296 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15298 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15300 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15301 this.store.on('load', this.onTouchViewLoad, this);
15302 this.store.on('loadexception', this.onTouchViewLoadException, this);
15304 if(this.hiddenName){
15306 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15308 this.hiddenField.dom.value =
15309 this.hiddenValue !== undefined ? this.hiddenValue :
15310 this.value !== undefined ? this.value : '';
15312 this.el.dom.removeAttribute('name');
15313 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15317 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15318 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15321 if(this.removable && !this.multiple){
15322 var close = this.closeTriggerEl();
15324 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15325 close.on('click', this.removeBtnClick, this, close);
15329 * fix the bug in Safari iOS8
15331 this.inputEl().on("focus", function(e){
15332 document.activeElement.blur();
15335 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15342 renderTouchView : function()
15344 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15345 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15347 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15348 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15350 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15351 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15352 this.touchViewBodyEl.setStyle('overflow', 'auto');
15354 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15355 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15357 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15358 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15362 showTouchView : function()
15368 this.touchViewHeaderEl.hide();
15370 if(this.modalTitle.length){
15371 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15372 this.touchViewHeaderEl.show();
15375 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15376 this.touchViewEl.show();
15378 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15380 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15381 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15383 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15385 if(this.modalTitle.length){
15386 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15389 this.touchViewBodyEl.setHeight(bodyHeight);
15393 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15395 this.touchViewEl.addClass('in');
15398 if(this._touchViewMask){
15399 Roo.get(document.body).addClass("x-body-masked");
15400 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15401 this._touchViewMask.setStyle('z-index', 10000);
15402 this._touchViewMask.addClass('show');
15405 this.doTouchViewQuery();
15409 hideTouchView : function()
15411 this.touchViewEl.removeClass('in');
15415 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15417 this.touchViewEl.setStyle('display', 'none');
15420 if(this._touchViewMask){
15421 this._touchViewMask.removeClass('show');
15422 Roo.get(document.body).removeClass("x-body-masked");
15426 setTouchViewValue : function()
15433 Roo.each(this.tickItems, function(o){
15438 this.hideTouchView();
15441 doTouchViewQuery : function()
15450 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15454 if(!this.alwaysQuery || this.mode == 'local'){
15455 this.onTouchViewLoad();
15462 onTouchViewBeforeLoad : function(combo,opts)
15468 onTouchViewLoad : function()
15470 if(this.store.getCount() < 1){
15471 this.onTouchViewEmptyResults();
15475 this.clearTouchView();
15477 var rawValue = this.getRawValue();
15479 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15481 this.tickItems = [];
15483 this.store.data.each(function(d, rowIndex){
15484 var row = this.touchViewListGroup.createChild(template);
15486 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15487 row.addClass(d.data.cls);
15490 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15493 html : d.data[this.displayField]
15496 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15497 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15500 row.removeClass('selected');
15501 if(!this.multiple && this.valueField &&
15502 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15505 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15506 row.addClass('selected');
15509 if(this.multiple && this.valueField &&
15510 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15514 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15515 this.tickItems.push(d.data);
15518 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15522 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15524 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15526 if(this.modalTitle.length){
15527 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15530 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15532 if(this.mobile_restrict_height && listHeight < bodyHeight){
15533 this.touchViewBodyEl.setHeight(listHeight);
15538 if(firstChecked && listHeight > bodyHeight){
15539 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15544 onTouchViewLoadException : function()
15546 this.hideTouchView();
15549 onTouchViewEmptyResults : function()
15551 this.clearTouchView();
15553 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15555 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15559 clearTouchView : function()
15561 this.touchViewListGroup.dom.innerHTML = '';
15564 onTouchViewClick : function(e, el, o)
15566 e.preventDefault();
15569 var rowIndex = o.rowIndex;
15571 var r = this.store.getAt(rowIndex);
15573 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15575 if(!this.multiple){
15576 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15577 c.dom.removeAttribute('checked');
15580 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15582 this.setFromData(r.data);
15584 var close = this.closeTriggerEl();
15590 this.hideTouchView();
15592 this.fireEvent('select', this, r, rowIndex);
15597 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15598 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15599 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15603 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15604 this.addItem(r.data);
15605 this.tickItems.push(r.data);
15609 getAutoCreateNativeIOS : function()
15612 cls: 'form-group' //input-group,
15617 cls : 'roo-ios-select'
15621 combobox.name = this.name;
15624 if (this.disabled) {
15625 combobox.disabled = true;
15628 var settings = this;
15630 ['xs','sm','md','lg'].map(function(size){
15631 if (settings[size]) {
15632 cfg.cls += ' col-' + size + '-' + settings[size];
15642 initIOSView : function()
15644 this.store.on('load', this.onIOSViewLoad, this);
15649 onIOSViewLoad : function()
15651 if(this.store.getCount() < 1){
15655 this.clearIOSView();
15657 if(this.allowBlank) {
15659 var default_text = '-- SELECT --';
15661 if(this.placeholder.length){
15662 default_text = this.placeholder;
15665 if(this.emptyTitle.length){
15666 default_text += ' - ' + this.emptyTitle + ' -';
15669 var opt = this.inputEl().createChild({
15672 html : default_text
15676 o[this.valueField] = 0;
15677 o[this.displayField] = default_text;
15679 this.ios_options.push({
15686 this.store.data.each(function(d, rowIndex){
15690 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15691 html = d.data[this.displayField];
15696 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15697 value = d.data[this.valueField];
15706 if(this.value == d.data[this.valueField]){
15707 option['selected'] = true;
15710 var opt = this.inputEl().createChild(option);
15712 this.ios_options.push({
15719 this.inputEl().on('change', function(){
15720 this.fireEvent('select', this);
15725 clearIOSView: function()
15727 this.inputEl().dom.innerHTML = '';
15729 this.ios_options = [];
15732 setIOSValue: function(v)
15736 if(!this.ios_options){
15740 Roo.each(this.ios_options, function(opts){
15742 opts.el.dom.removeAttribute('selected');
15744 if(opts.data[this.valueField] != v){
15748 opts.el.dom.setAttribute('selected', true);
15754 * @cfg {Boolean} grow
15758 * @cfg {Number} growMin
15762 * @cfg {Number} growMax
15771 Roo.apply(Roo.bootstrap.ComboBox, {
15775 cls: 'modal-header',
15797 cls: 'list-group-item',
15801 cls: 'roo-combobox-list-group-item-value'
15805 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15819 listItemCheckbox : {
15821 cls: 'list-group-item',
15825 cls: 'roo-combobox-list-group-item-value'
15829 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15845 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15850 cls: 'modal-footer',
15858 cls: 'col-xs-6 text-left',
15861 cls: 'btn btn-danger roo-touch-view-cancel',
15867 cls: 'col-xs-6 text-right',
15870 cls: 'btn btn-success roo-touch-view-ok',
15881 Roo.apply(Roo.bootstrap.ComboBox, {
15883 touchViewTemplate : {
15885 cls: 'modal fade roo-combobox-touch-view',
15889 cls: 'modal-dialog',
15890 style : 'position:fixed', // we have to fix position....
15894 cls: 'modal-content',
15896 Roo.bootstrap.ComboBox.header,
15897 Roo.bootstrap.ComboBox.body,
15898 Roo.bootstrap.ComboBox.footer
15907 * Ext JS Library 1.1.1
15908 * Copyright(c) 2006-2007, Ext JS, LLC.
15910 * Originally Released Under LGPL - original licence link has changed is not relivant.
15913 * <script type="text/javascript">
15918 * @extends Roo.util.Observable
15919 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15920 * This class also supports single and multi selection modes. <br>
15921 * Create a data model bound view:
15923 var store = new Roo.data.Store(...);
15925 var view = new Roo.View({
15927 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15929 singleSelect: true,
15930 selectedClass: "ydataview-selected",
15934 // listen for node click?
15935 view.on("click", function(vw, index, node, e){
15936 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15940 dataModel.load("foobar.xml");
15942 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15944 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15945 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15947 * Note: old style constructor is still suported (container, template, config)
15950 * Create a new View
15951 * @param {Object} config The config object
15954 Roo.View = function(config, depreciated_tpl, depreciated_config){
15956 this.parent = false;
15958 if (typeof(depreciated_tpl) == 'undefined') {
15959 // new way.. - universal constructor.
15960 Roo.apply(this, config);
15961 this.el = Roo.get(this.el);
15964 this.el = Roo.get(config);
15965 this.tpl = depreciated_tpl;
15966 Roo.apply(this, depreciated_config);
15968 this.wrapEl = this.el.wrap().wrap();
15969 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15972 if(typeof(this.tpl) == "string"){
15973 this.tpl = new Roo.Template(this.tpl);
15975 // support xtype ctors..
15976 this.tpl = new Roo.factory(this.tpl, Roo);
15980 this.tpl.compile();
15985 * @event beforeclick
15986 * Fires before a click is processed. Returns false to cancel the default action.
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
15992 "beforeclick" : true,
15995 * Fires when a template node is 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
16004 * Fires when a template node is double 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
16012 * @event contextmenu
16013 * Fires when a template node is right clicked.
16014 * @param {Roo.View} this
16015 * @param {Number} index The index of the target node
16016 * @param {HTMLElement} node The target node
16017 * @param {Roo.EventObject} e The raw event object
16019 "contextmenu" : true,
16021 * @event selectionchange
16022 * Fires when the selected nodes change.
16023 * @param {Roo.View} this
16024 * @param {Array} selections Array of the selected nodes
16026 "selectionchange" : true,
16029 * @event beforeselect
16030 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16031 * @param {Roo.View} this
16032 * @param {HTMLElement} node The node to be selected
16033 * @param {Array} selections Array of currently selected nodes
16035 "beforeselect" : true,
16037 * @event preparedata
16038 * Fires on every row to render, to allow you to change the data.
16039 * @param {Roo.View} this
16040 * @param {Object} data to be rendered (change this)
16042 "preparedata" : true
16050 "click": this.onClick,
16051 "dblclick": this.onDblClick,
16052 "contextmenu": this.onContextMenu,
16056 this.selections = [];
16058 this.cmp = new Roo.CompositeElementLite([]);
16060 this.store = Roo.factory(this.store, Roo.data);
16061 this.setStore(this.store, true);
16064 if ( this.footer && this.footer.xtype) {
16066 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16068 this.footer.dataSource = this.store;
16069 this.footer.container = fctr;
16070 this.footer = Roo.factory(this.footer, Roo);
16071 fctr.insertFirst(this.el);
16073 // this is a bit insane - as the paging toolbar seems to detach the el..
16074 // dom.parentNode.parentNode.parentNode
16075 // they get detached?
16079 Roo.View.superclass.constructor.call(this);
16084 Roo.extend(Roo.View, Roo.util.Observable, {
16087 * @cfg {Roo.data.Store} store Data store to load data from.
16092 * @cfg {String|Roo.Element} el The container element.
16097 * @cfg {String|Roo.Template} tpl The template used by this View
16101 * @cfg {String} dataName the named area of the template to use as the data area
16102 * Works with domtemplates roo-name="name"
16106 * @cfg {String} selectedClass The css class to add to selected nodes
16108 selectedClass : "x-view-selected",
16110 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16115 * @cfg {String} text to display on mask (default Loading)
16119 * @cfg {Boolean} multiSelect Allow multiple selection
16121 multiSelect : false,
16123 * @cfg {Boolean} singleSelect Allow single selection
16125 singleSelect: false,
16128 * @cfg {Boolean} toggleSelect - selecting
16130 toggleSelect : false,
16133 * @cfg {Boolean} tickable - selecting
16138 * Returns the element this view is bound to.
16139 * @return {Roo.Element}
16141 getEl : function(){
16142 return this.wrapEl;
16148 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16150 refresh : function(){
16151 //Roo.log('refresh');
16154 // if we are using something like 'domtemplate', then
16155 // the what gets used is:
16156 // t.applySubtemplate(NAME, data, wrapping data..)
16157 // the outer template then get' applied with
16158 // the store 'extra data'
16159 // and the body get's added to the
16160 // roo-name="data" node?
16161 // <span class='roo-tpl-{name}'></span> ?????
16165 this.clearSelections();
16166 this.el.update("");
16168 var records = this.store.getRange();
16169 if(records.length < 1) {
16171 // is this valid?? = should it render a template??
16173 this.el.update(this.emptyText);
16177 if (this.dataName) {
16178 this.el.update(t.apply(this.store.meta)); //????
16179 el = this.el.child('.roo-tpl-' + this.dataName);
16182 for(var i = 0, len = records.length; i < len; i++){
16183 var data = this.prepareData(records[i].data, i, records[i]);
16184 this.fireEvent("preparedata", this, data, i, records[i]);
16186 var d = Roo.apply({}, data);
16189 Roo.apply(d, {'roo-id' : Roo.id()});
16193 Roo.each(this.parent.item, function(item){
16194 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16197 Roo.apply(d, {'roo-data-checked' : 'checked'});
16201 html[html.length] = Roo.util.Format.trim(
16203 t.applySubtemplate(this.dataName, d, this.store.meta) :
16210 el.update(html.join(""));
16211 this.nodes = el.dom.childNodes;
16212 this.updateIndexes(0);
16217 * Function to override to reformat the data that is sent to
16218 * the template for each node.
16219 * DEPRICATED - use the preparedata event handler.
16220 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16221 * a JSON object for an UpdateManager bound view).
16223 prepareData : function(data, index, record)
16225 this.fireEvent("preparedata", this, data, index, record);
16229 onUpdate : function(ds, record){
16230 // Roo.log('on update');
16231 this.clearSelections();
16232 var index = this.store.indexOf(record);
16233 var n = this.nodes[index];
16234 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16235 n.parentNode.removeChild(n);
16236 this.updateIndexes(index, index);
16242 onAdd : function(ds, records, index)
16244 //Roo.log(['on Add', ds, records, index] );
16245 this.clearSelections();
16246 if(this.nodes.length == 0){
16250 var n = this.nodes[index];
16251 for(var i = 0, len = records.length; i < len; i++){
16252 var d = this.prepareData(records[i].data, i, records[i]);
16254 this.tpl.insertBefore(n, d);
16257 this.tpl.append(this.el, d);
16260 this.updateIndexes(index);
16263 onRemove : function(ds, record, index){
16264 // Roo.log('onRemove');
16265 this.clearSelections();
16266 var el = this.dataName ?
16267 this.el.child('.roo-tpl-' + this.dataName) :
16270 el.dom.removeChild(this.nodes[index]);
16271 this.updateIndexes(index);
16275 * Refresh an individual node.
16276 * @param {Number} index
16278 refreshNode : function(index){
16279 this.onUpdate(this.store, this.store.getAt(index));
16282 updateIndexes : function(startIndex, endIndex){
16283 var ns = this.nodes;
16284 startIndex = startIndex || 0;
16285 endIndex = endIndex || ns.length - 1;
16286 for(var i = startIndex; i <= endIndex; i++){
16287 ns[i].nodeIndex = i;
16292 * Changes the data store this view uses and refresh the view.
16293 * @param {Store} store
16295 setStore : function(store, initial){
16296 if(!initial && this.store){
16297 this.store.un("datachanged", this.refresh);
16298 this.store.un("add", this.onAdd);
16299 this.store.un("remove", this.onRemove);
16300 this.store.un("update", this.onUpdate);
16301 this.store.un("clear", this.refresh);
16302 this.store.un("beforeload", this.onBeforeLoad);
16303 this.store.un("load", this.onLoad);
16304 this.store.un("loadexception", this.onLoad);
16308 store.on("datachanged", this.refresh, this);
16309 store.on("add", this.onAdd, this);
16310 store.on("remove", this.onRemove, this);
16311 store.on("update", this.onUpdate, this);
16312 store.on("clear", this.refresh, this);
16313 store.on("beforeload", this.onBeforeLoad, this);
16314 store.on("load", this.onLoad, this);
16315 store.on("loadexception", this.onLoad, this);
16323 * onbeforeLoad - masks the loading area.
16326 onBeforeLoad : function(store,opts)
16328 //Roo.log('onBeforeLoad');
16330 this.el.update("");
16332 this.el.mask(this.mask ? this.mask : "Loading" );
16334 onLoad : function ()
16341 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16342 * @param {HTMLElement} node
16343 * @return {HTMLElement} The template node
16345 findItemFromChild : function(node){
16346 var el = this.dataName ?
16347 this.el.child('.roo-tpl-' + this.dataName,true) :
16350 if(!node || node.parentNode == el){
16353 var p = node.parentNode;
16354 while(p && p != el){
16355 if(p.parentNode == el){
16364 onClick : function(e){
16365 var item = this.findItemFromChild(e.getTarget());
16367 var index = this.indexOf(item);
16368 if(this.onItemClick(item, index, e) !== false){
16369 this.fireEvent("click", this, index, item, e);
16372 this.clearSelections();
16377 onContextMenu : function(e){
16378 var item = this.findItemFromChild(e.getTarget());
16380 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16385 onDblClick : function(e){
16386 var item = this.findItemFromChild(e.getTarget());
16388 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16392 onItemClick : function(item, index, e)
16394 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16397 if (this.toggleSelect) {
16398 var m = this.isSelected(item) ? 'unselect' : 'select';
16401 _t[m](item, true, false);
16404 if(this.multiSelect || this.singleSelect){
16405 if(this.multiSelect && e.shiftKey && this.lastSelection){
16406 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16408 this.select(item, this.multiSelect && e.ctrlKey);
16409 this.lastSelection = item;
16412 if(!this.tickable){
16413 e.preventDefault();
16421 * Get the number of selected nodes.
16424 getSelectionCount : function(){
16425 return this.selections.length;
16429 * Get the currently selected nodes.
16430 * @return {Array} An array of HTMLElements
16432 getSelectedNodes : function(){
16433 return this.selections;
16437 * Get the indexes of the selected nodes.
16440 getSelectedIndexes : function(){
16441 var indexes = [], s = this.selections;
16442 for(var i = 0, len = s.length; i < len; i++){
16443 indexes.push(s[i].nodeIndex);
16449 * Clear all selections
16450 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16452 clearSelections : function(suppressEvent){
16453 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16454 this.cmp.elements = this.selections;
16455 this.cmp.removeClass(this.selectedClass);
16456 this.selections = [];
16457 if(!suppressEvent){
16458 this.fireEvent("selectionchange", this, this.selections);
16464 * Returns true if the passed node is selected
16465 * @param {HTMLElement/Number} node The node or node index
16466 * @return {Boolean}
16468 isSelected : function(node){
16469 var s = this.selections;
16473 node = this.getNode(node);
16474 return s.indexOf(node) !== -1;
16479 * @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
16480 * @param {Boolean} keepExisting (optional) true to keep existing selections
16481 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16483 select : function(nodeInfo, keepExisting, suppressEvent){
16484 if(nodeInfo instanceof Array){
16486 this.clearSelections(true);
16488 for(var i = 0, len = nodeInfo.length; i < len; i++){
16489 this.select(nodeInfo[i], true, true);
16493 var node = this.getNode(nodeInfo);
16494 if(!node || this.isSelected(node)){
16495 return; // already selected.
16498 this.clearSelections(true);
16501 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16502 Roo.fly(node).addClass(this.selectedClass);
16503 this.selections.push(node);
16504 if(!suppressEvent){
16505 this.fireEvent("selectionchange", this, this.selections);
16513 * @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
16514 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16515 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16517 unselect : function(nodeInfo, keepExisting, suppressEvent)
16519 if(nodeInfo instanceof Array){
16520 Roo.each(this.selections, function(s) {
16521 this.unselect(s, nodeInfo);
16525 var node = this.getNode(nodeInfo);
16526 if(!node || !this.isSelected(node)){
16527 //Roo.log("not selected");
16528 return; // not selected.
16532 Roo.each(this.selections, function(s) {
16534 Roo.fly(node).removeClass(this.selectedClass);
16541 this.selections= ns;
16542 this.fireEvent("selectionchange", this, this.selections);
16546 * Gets a template node.
16547 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16548 * @return {HTMLElement} The node or null if it wasn't found
16550 getNode : function(nodeInfo){
16551 if(typeof nodeInfo == "string"){
16552 return document.getElementById(nodeInfo);
16553 }else if(typeof nodeInfo == "number"){
16554 return this.nodes[nodeInfo];
16560 * Gets a range template nodes.
16561 * @param {Number} startIndex
16562 * @param {Number} endIndex
16563 * @return {Array} An array of nodes
16565 getNodes : function(start, end){
16566 var ns = this.nodes;
16567 start = start || 0;
16568 end = typeof end == "undefined" ? ns.length - 1 : end;
16571 for(var i = start; i <= end; i++){
16575 for(var i = start; i >= end; i--){
16583 * Finds the index of the passed node
16584 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16585 * @return {Number} The index of the node or -1
16587 indexOf : function(node){
16588 node = this.getNode(node);
16589 if(typeof node.nodeIndex == "number"){
16590 return node.nodeIndex;
16592 var ns = this.nodes;
16593 for(var i = 0, len = ns.length; i < len; i++){
16604 * based on jquery fullcalendar
16608 Roo.bootstrap = Roo.bootstrap || {};
16610 * @class Roo.bootstrap.Calendar
16611 * @extends Roo.bootstrap.Component
16612 * Bootstrap Calendar class
16613 * @cfg {Boolean} loadMask (true|false) default false
16614 * @cfg {Object} header generate the user specific header of the calendar, default false
16617 * Create a new Container
16618 * @param {Object} config The config object
16623 Roo.bootstrap.Calendar = function(config){
16624 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16628 * Fires when a date is selected
16629 * @param {DatePicker} this
16630 * @param {Date} date The selected date
16634 * @event monthchange
16635 * Fires when the displayed month changes
16636 * @param {DatePicker} this
16637 * @param {Date} date The selected month
16639 'monthchange': true,
16641 * @event evententer
16642 * Fires when mouse over an event
16643 * @param {Calendar} this
16644 * @param {event} Event
16646 'evententer': true,
16648 * @event eventleave
16649 * Fires when the mouse leaves an
16650 * @param {Calendar} this
16653 'eventleave': true,
16655 * @event eventclick
16656 * Fires when the mouse click an
16657 * @param {Calendar} this
16666 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16669 * @cfg {Number} startDay
16670 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16678 getAutoCreate : function(){
16681 var fc_button = function(name, corner, style, content ) {
16682 return Roo.apply({},{
16684 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16686 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16689 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16700 style : 'width:100%',
16707 cls : 'fc-header-left',
16709 fc_button('prev', 'left', 'arrow', '‹' ),
16710 fc_button('next', 'right', 'arrow', '›' ),
16711 { tag: 'span', cls: 'fc-header-space' },
16712 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16720 cls : 'fc-header-center',
16724 cls: 'fc-header-title',
16727 html : 'month / year'
16735 cls : 'fc-header-right',
16737 /* fc_button('month', 'left', '', 'month' ),
16738 fc_button('week', '', '', 'week' ),
16739 fc_button('day', 'right', '', 'day' )
16751 header = this.header;
16754 var cal_heads = function() {
16756 // fixme - handle this.
16758 for (var i =0; i < Date.dayNames.length; i++) {
16759 var d = Date.dayNames[i];
16762 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16763 html : d.substring(0,3)
16767 ret[0].cls += ' fc-first';
16768 ret[6].cls += ' fc-last';
16771 var cal_cell = function(n) {
16774 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16779 cls: 'fc-day-number',
16783 cls: 'fc-day-content',
16787 style: 'position: relative;' // height: 17px;
16799 var cal_rows = function() {
16802 for (var r = 0; r < 6; r++) {
16809 for (var i =0; i < Date.dayNames.length; i++) {
16810 var d = Date.dayNames[i];
16811 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16814 row.cn[0].cls+=' fc-first';
16815 row.cn[0].cn[0].style = 'min-height:90px';
16816 row.cn[6].cls+=' fc-last';
16820 ret[0].cls += ' fc-first';
16821 ret[4].cls += ' fc-prev-last';
16822 ret[5].cls += ' fc-last';
16829 cls: 'fc-border-separate',
16830 style : 'width:100%',
16838 cls : 'fc-first fc-last',
16856 cls : 'fc-content',
16857 style : "position: relative;",
16860 cls : 'fc-view fc-view-month fc-grid',
16861 style : 'position: relative',
16862 unselectable : 'on',
16865 cls : 'fc-event-container',
16866 style : 'position:absolute;z-index:8;top:0;left:0;'
16884 initEvents : function()
16887 throw "can not find store for calendar";
16893 style: "text-align:center",
16897 style: "background-color:white;width:50%;margin:250 auto",
16901 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16912 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16914 var size = this.el.select('.fc-content', true).first().getSize();
16915 this.maskEl.setSize(size.width, size.height);
16916 this.maskEl.enableDisplayMode("block");
16917 if(!this.loadMask){
16918 this.maskEl.hide();
16921 this.store = Roo.factory(this.store, Roo.data);
16922 this.store.on('load', this.onLoad, this);
16923 this.store.on('beforeload', this.onBeforeLoad, this);
16927 this.cells = this.el.select('.fc-day',true);
16928 //Roo.log(this.cells);
16929 this.textNodes = this.el.query('.fc-day-number');
16930 this.cells.addClassOnOver('fc-state-hover');
16932 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16933 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16934 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16935 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16937 this.on('monthchange', this.onMonthChange, this);
16939 this.update(new Date().clearTime());
16942 resize : function() {
16943 var sz = this.el.getSize();
16945 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16946 this.el.select('.fc-day-content div',true).setHeight(34);
16951 showPrevMonth : function(e){
16952 this.update(this.activeDate.add("mo", -1));
16954 showToday : function(e){
16955 this.update(new Date().clearTime());
16958 showNextMonth : function(e){
16959 this.update(this.activeDate.add("mo", 1));
16963 showPrevYear : function(){
16964 this.update(this.activeDate.add("y", -1));
16968 showNextYear : function(){
16969 this.update(this.activeDate.add("y", 1));
16974 update : function(date)
16976 var vd = this.activeDate;
16977 this.activeDate = date;
16978 // if(vd && this.el){
16979 // var t = date.getTime();
16980 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16981 // Roo.log('using add remove');
16983 // this.fireEvent('monthchange', this, date);
16985 // this.cells.removeClass("fc-state-highlight");
16986 // this.cells.each(function(c){
16987 // if(c.dateValue == t){
16988 // c.addClass("fc-state-highlight");
16989 // setTimeout(function(){
16990 // try{c.dom.firstChild.focus();}catch(e){}
17000 var days = date.getDaysInMonth();
17002 var firstOfMonth = date.getFirstDateOfMonth();
17003 var startingPos = firstOfMonth.getDay()-this.startDay;
17005 if(startingPos < this.startDay){
17009 var pm = date.add(Date.MONTH, -1);
17010 var prevStart = pm.getDaysInMonth()-startingPos;
17012 this.cells = this.el.select('.fc-day',true);
17013 this.textNodes = this.el.query('.fc-day-number');
17014 this.cells.addClassOnOver('fc-state-hover');
17016 var cells = this.cells.elements;
17017 var textEls = this.textNodes;
17019 Roo.each(cells, function(cell){
17020 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17023 days += startingPos;
17025 // convert everything to numbers so it's fast
17026 var day = 86400000;
17027 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17030 //Roo.log(prevStart);
17032 var today = new Date().clearTime().getTime();
17033 var sel = date.clearTime().getTime();
17034 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17035 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17036 var ddMatch = this.disabledDatesRE;
17037 var ddText = this.disabledDatesText;
17038 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17039 var ddaysText = this.disabledDaysText;
17040 var format = this.format;
17042 var setCellClass = function(cal, cell){
17046 //Roo.log('set Cell Class');
17048 var t = d.getTime();
17052 cell.dateValue = t;
17054 cell.className += " fc-today";
17055 cell.className += " fc-state-highlight";
17056 cell.title = cal.todayText;
17059 // disable highlight in other month..
17060 //cell.className += " fc-state-highlight";
17065 cell.className = " fc-state-disabled";
17066 cell.title = cal.minText;
17070 cell.className = " fc-state-disabled";
17071 cell.title = cal.maxText;
17075 if(ddays.indexOf(d.getDay()) != -1){
17076 cell.title = ddaysText;
17077 cell.className = " fc-state-disabled";
17080 if(ddMatch && format){
17081 var fvalue = d.dateFormat(format);
17082 if(ddMatch.test(fvalue)){
17083 cell.title = ddText.replace("%0", fvalue);
17084 cell.className = " fc-state-disabled";
17088 if (!cell.initialClassName) {
17089 cell.initialClassName = cell.dom.className;
17092 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17097 for(; i < startingPos; i++) {
17098 textEls[i].innerHTML = (++prevStart);
17099 d.setDate(d.getDate()+1);
17101 cells[i].className = "fc-past fc-other-month";
17102 setCellClass(this, cells[i]);
17107 for(; i < days; i++){
17108 intDay = i - startingPos + 1;
17109 textEls[i].innerHTML = (intDay);
17110 d.setDate(d.getDate()+1);
17112 cells[i].className = ''; // "x-date-active";
17113 setCellClass(this, cells[i]);
17117 for(; i < 42; i++) {
17118 textEls[i].innerHTML = (++extraDays);
17119 d.setDate(d.getDate()+1);
17121 cells[i].className = "fc-future fc-other-month";
17122 setCellClass(this, cells[i]);
17125 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17127 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17129 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17130 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17132 if(totalRows != 6){
17133 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17134 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17137 this.fireEvent('monthchange', this, date);
17141 if(!this.internalRender){
17142 var main = this.el.dom.firstChild;
17143 var w = main.offsetWidth;
17144 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17145 Roo.fly(main).setWidth(w);
17146 this.internalRender = true;
17147 // opera does not respect the auto grow header center column
17148 // then, after it gets a width opera refuses to recalculate
17149 // without a second pass
17150 if(Roo.isOpera && !this.secondPass){
17151 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17152 this.secondPass = true;
17153 this.update.defer(10, this, [date]);
17160 findCell : function(dt) {
17161 dt = dt.clearTime().getTime();
17163 this.cells.each(function(c){
17164 //Roo.log("check " +c.dateValue + '?=' + dt);
17165 if(c.dateValue == dt){
17175 findCells : function(ev) {
17176 var s = ev.start.clone().clearTime().getTime();
17178 var e= ev.end.clone().clearTime().getTime();
17181 this.cells.each(function(c){
17182 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17184 if(c.dateValue > e){
17187 if(c.dateValue < s){
17196 // findBestRow: function(cells)
17200 // for (var i =0 ; i < cells.length;i++) {
17201 // ret = Math.max(cells[i].rows || 0,ret);
17208 addItem : function(ev)
17210 // look for vertical location slot in
17211 var cells = this.findCells(ev);
17213 // ev.row = this.findBestRow(cells);
17215 // work out the location.
17219 for(var i =0; i < cells.length; i++) {
17221 cells[i].row = cells[0].row;
17224 cells[i].row = cells[i].row + 1;
17234 if (crow.start.getY() == cells[i].getY()) {
17236 crow.end = cells[i];
17253 cells[0].events.push(ev);
17255 this.calevents.push(ev);
17258 clearEvents: function() {
17260 if(!this.calevents){
17264 Roo.each(this.cells.elements, function(c){
17270 Roo.each(this.calevents, function(e) {
17271 Roo.each(e.els, function(el) {
17272 el.un('mouseenter' ,this.onEventEnter, this);
17273 el.un('mouseleave' ,this.onEventLeave, this);
17278 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17284 renderEvents: function()
17288 this.cells.each(function(c) {
17297 if(c.row != c.events.length){
17298 r = 4 - (4 - (c.row - c.events.length));
17301 c.events = ev.slice(0, r);
17302 c.more = ev.slice(r);
17304 if(c.more.length && c.more.length == 1){
17305 c.events.push(c.more.pop());
17308 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17312 this.cells.each(function(c) {
17314 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17317 for (var e = 0; e < c.events.length; e++){
17318 var ev = c.events[e];
17319 var rows = ev.rows;
17321 for(var i = 0; i < rows.length; i++) {
17323 // how many rows should it span..
17326 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17327 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17329 unselectable : "on",
17332 cls: 'fc-event-inner',
17336 // cls: 'fc-event-time',
17337 // html : cells.length > 1 ? '' : ev.time
17341 cls: 'fc-event-title',
17342 html : String.format('{0}', ev.title)
17349 cls: 'ui-resizable-handle ui-resizable-e',
17350 html : '  '
17357 cfg.cls += ' fc-event-start';
17359 if ((i+1) == rows.length) {
17360 cfg.cls += ' fc-event-end';
17363 var ctr = _this.el.select('.fc-event-container',true).first();
17364 var cg = ctr.createChild(cfg);
17366 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17367 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17369 var r = (c.more.length) ? 1 : 0;
17370 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17371 cg.setWidth(ebox.right - sbox.x -2);
17373 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17374 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17375 cg.on('click', _this.onEventClick, _this, ev);
17386 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17387 style : 'position: absolute',
17388 unselectable : "on",
17391 cls: 'fc-event-inner',
17395 cls: 'fc-event-title',
17403 cls: 'ui-resizable-handle ui-resizable-e',
17404 html : '  '
17410 var ctr = _this.el.select('.fc-event-container',true).first();
17411 var cg = ctr.createChild(cfg);
17413 var sbox = c.select('.fc-day-content',true).first().getBox();
17414 var ebox = c.select('.fc-day-content',true).first().getBox();
17416 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17417 cg.setWidth(ebox.right - sbox.x -2);
17419 cg.on('click', _this.onMoreEventClick, _this, c.more);
17429 onEventEnter: function (e, el,event,d) {
17430 this.fireEvent('evententer', this, el, event);
17433 onEventLeave: function (e, el,event,d) {
17434 this.fireEvent('eventleave', this, el, event);
17437 onEventClick: function (e, el,event,d) {
17438 this.fireEvent('eventclick', this, el, event);
17441 onMonthChange: function () {
17445 onMoreEventClick: function(e, el, more)
17449 this.calpopover.placement = 'right';
17450 this.calpopover.setTitle('More');
17452 this.calpopover.setContent('');
17454 var ctr = this.calpopover.el.select('.popover-content', true).first();
17456 Roo.each(more, function(m){
17458 cls : 'fc-event-hori fc-event-draggable',
17461 var cg = ctr.createChild(cfg);
17463 cg.on('click', _this.onEventClick, _this, m);
17466 this.calpopover.show(el);
17471 onLoad: function ()
17473 this.calevents = [];
17476 if(this.store.getCount() > 0){
17477 this.store.data.each(function(d){
17480 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17481 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17482 time : d.data.start_time,
17483 title : d.data.title,
17484 description : d.data.description,
17485 venue : d.data.venue
17490 this.renderEvents();
17492 if(this.calevents.length && this.loadMask){
17493 this.maskEl.hide();
17497 onBeforeLoad: function()
17499 this.clearEvents();
17501 this.maskEl.show();
17515 * @class Roo.bootstrap.Popover
17516 * @extends Roo.bootstrap.Component
17517 * Bootstrap Popover class
17518 * @cfg {String} html contents of the popover (or false to use children..)
17519 * @cfg {String} title of popover (or false to hide)
17520 * @cfg {String} placement how it is placed
17521 * @cfg {String} trigger click || hover (or false to trigger manually)
17522 * @cfg {String} over what (parent or false to trigger manually.)
17523 * @cfg {Number} delay - delay before showing
17526 * Create a new Popover
17527 * @param {Object} config The config object
17530 Roo.bootstrap.Popover = function(config){
17531 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17537 * After the popover show
17539 * @param {Roo.bootstrap.Popover} this
17544 * After the popover hide
17546 * @param {Roo.bootstrap.Popover} this
17552 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17554 title: 'Fill in a title',
17557 placement : 'right',
17558 trigger : 'hover', // hover
17564 can_build_overlaid : false,
17566 getChildContainer : function()
17568 return this.el.select('.popover-content',true).first();
17571 getAutoCreate : function(){
17574 cls : 'popover roo-dynamic',
17575 style: 'display:block',
17581 cls : 'popover-inner',
17585 cls: 'popover-title',
17589 cls : 'popover-content',
17600 setTitle: function(str)
17603 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17605 setContent: function(str)
17608 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17610 // as it get's added to the bottom of the page.
17611 onRender : function(ct, position)
17613 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17615 var cfg = Roo.apply({}, this.getAutoCreate());
17619 cfg.cls += ' ' + this.cls;
17622 cfg.style = this.style;
17624 //Roo.log("adding to ");
17625 this.el = Roo.get(document.body).createChild(cfg, position);
17626 // Roo.log(this.el);
17631 initEvents : function()
17633 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17634 this.el.enableDisplayMode('block');
17636 if (this.over === false) {
17639 if (this.triggers === false) {
17642 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17643 var triggers = this.trigger ? this.trigger.split(' ') : [];
17644 Roo.each(triggers, function(trigger) {
17646 if (trigger == 'click') {
17647 on_el.on('click', this.toggle, this);
17648 } else if (trigger != 'manual') {
17649 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17650 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17652 on_el.on(eventIn ,this.enter, this);
17653 on_el.on(eventOut, this.leave, this);
17664 toggle : function () {
17665 this.hoverState == 'in' ? this.leave() : this.enter();
17668 enter : function () {
17670 clearTimeout(this.timeout);
17672 this.hoverState = 'in';
17674 if (!this.delay || !this.delay.show) {
17679 this.timeout = setTimeout(function () {
17680 if (_t.hoverState == 'in') {
17683 }, this.delay.show)
17686 leave : function() {
17687 clearTimeout(this.timeout);
17689 this.hoverState = 'out';
17691 if (!this.delay || !this.delay.hide) {
17696 this.timeout = setTimeout(function () {
17697 if (_t.hoverState == 'out') {
17700 }, this.delay.hide)
17703 show : function (on_el)
17706 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17710 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17711 if (this.html !== false) {
17712 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17714 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17715 if (!this.title.length) {
17716 this.el.select('.popover-title',true).hide();
17719 var placement = typeof this.placement == 'function' ?
17720 this.placement.call(this, this.el, on_el) :
17723 var autoToken = /\s?auto?\s?/i;
17724 var autoPlace = autoToken.test(placement);
17726 placement = placement.replace(autoToken, '') || 'top';
17730 //this.el.setXY([0,0]);
17732 this.el.dom.style.display='block';
17733 this.el.addClass(placement);
17735 //this.el.appendTo(on_el);
17737 var p = this.getPosition();
17738 var box = this.el.getBox();
17743 var align = Roo.bootstrap.Popover.alignment[placement];
17746 this.el.alignTo(on_el, align[0],align[1]);
17747 //var arrow = this.el.select('.arrow',true).first();
17748 //arrow.set(align[2],
17750 this.el.addClass('in');
17753 if (this.el.hasClass('fade')) {
17757 this.hoverState = 'in';
17759 this.fireEvent('show', this);
17764 this.el.setXY([0,0]);
17765 this.el.removeClass('in');
17767 this.hoverState = null;
17769 this.fireEvent('hide', this);
17774 Roo.bootstrap.Popover.alignment = {
17775 'left' : ['r-l', [-10,0], 'right'],
17776 'right' : ['l-r', [10,0], 'left'],
17777 'bottom' : ['t-b', [0,10], 'top'],
17778 'top' : [ 'b-t', [0,-10], 'bottom']
17789 * @class Roo.bootstrap.Progress
17790 * @extends Roo.bootstrap.Component
17791 * Bootstrap Progress class
17792 * @cfg {Boolean} striped striped of the progress bar
17793 * @cfg {Boolean} active animated of the progress bar
17797 * Create a new Progress
17798 * @param {Object} config The config object
17801 Roo.bootstrap.Progress = function(config){
17802 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17805 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17810 getAutoCreate : function(){
17818 cfg.cls += ' progress-striped';
17822 cfg.cls += ' active';
17841 * @class Roo.bootstrap.ProgressBar
17842 * @extends Roo.bootstrap.Component
17843 * Bootstrap ProgressBar class
17844 * @cfg {Number} aria_valuenow aria-value now
17845 * @cfg {Number} aria_valuemin aria-value min
17846 * @cfg {Number} aria_valuemax aria-value max
17847 * @cfg {String} label label for the progress bar
17848 * @cfg {String} panel (success | info | warning | danger )
17849 * @cfg {String} role role of the progress bar
17850 * @cfg {String} sr_only text
17854 * Create a new ProgressBar
17855 * @param {Object} config The config object
17858 Roo.bootstrap.ProgressBar = function(config){
17859 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17862 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17866 aria_valuemax : 100,
17872 getAutoCreate : function()
17877 cls: 'progress-bar',
17878 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17890 cfg.role = this.role;
17893 if(this.aria_valuenow){
17894 cfg['aria-valuenow'] = this.aria_valuenow;
17897 if(this.aria_valuemin){
17898 cfg['aria-valuemin'] = this.aria_valuemin;
17901 if(this.aria_valuemax){
17902 cfg['aria-valuemax'] = this.aria_valuemax;
17905 if(this.label && !this.sr_only){
17906 cfg.html = this.label;
17910 cfg.cls += ' progress-bar-' + this.panel;
17916 update : function(aria_valuenow)
17918 this.aria_valuenow = aria_valuenow;
17920 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17935 * @class Roo.bootstrap.TabGroup
17936 * @extends Roo.bootstrap.Column
17937 * Bootstrap Column class
17938 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17939 * @cfg {Boolean} carousel true to make the group behave like a carousel
17940 * @cfg {Boolean} bullets show bullets for the panels
17941 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17942 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17943 * @cfg {Boolean} showarrow (true|false) show arrow default true
17946 * Create a new TabGroup
17947 * @param {Object} config The config object
17950 Roo.bootstrap.TabGroup = function(config){
17951 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17953 this.navId = Roo.id();
17956 Roo.bootstrap.TabGroup.register(this);
17960 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17963 transition : false,
17968 slideOnTouch : false,
17971 getAutoCreate : function()
17973 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17975 cfg.cls += ' tab-content';
17977 if (this.carousel) {
17978 cfg.cls += ' carousel slide';
17981 cls : 'carousel-inner',
17985 if(this.bullets && !Roo.isTouch){
17988 cls : 'carousel-bullets',
17992 if(this.bullets_cls){
17993 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18000 cfg.cn[0].cn.push(bullets);
18003 if(this.showarrow){
18004 cfg.cn[0].cn.push({
18006 class : 'carousel-arrow',
18010 class : 'carousel-prev',
18014 class : 'fa fa-chevron-left'
18020 class : 'carousel-next',
18024 class : 'fa fa-chevron-right'
18037 initEvents: function()
18039 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18040 // this.el.on("touchstart", this.onTouchStart, this);
18043 if(this.autoslide){
18046 this.slideFn = window.setInterval(function() {
18047 _this.showPanelNext();
18051 if(this.showarrow){
18052 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18053 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18059 // onTouchStart : function(e, el, o)
18061 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18065 // this.showPanelNext();
18069 getChildContainer : function()
18071 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18075 * register a Navigation item
18076 * @param {Roo.bootstrap.NavItem} the navitem to add
18078 register : function(item)
18080 this.tabs.push( item);
18081 item.navId = this.navId; // not really needed..
18086 getActivePanel : function()
18089 Roo.each(this.tabs, function(t) {
18099 getPanelByName : function(n)
18102 Roo.each(this.tabs, function(t) {
18103 if (t.tabId == n) {
18111 indexOfPanel : function(p)
18114 Roo.each(this.tabs, function(t,i) {
18115 if (t.tabId == p.tabId) {
18124 * show a specific panel
18125 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18126 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18128 showPanel : function (pan)
18130 if(this.transition || typeof(pan) == 'undefined'){
18131 Roo.log("waiting for the transitionend");
18135 if (typeof(pan) == 'number') {
18136 pan = this.tabs[pan];
18139 if (typeof(pan) == 'string') {
18140 pan = this.getPanelByName(pan);
18143 var cur = this.getActivePanel();
18146 Roo.log('pan or acitve pan is undefined');
18150 if (pan.tabId == this.getActivePanel().tabId) {
18154 if (false === cur.fireEvent('beforedeactivate')) {
18158 if(this.bullets > 0 && !Roo.isTouch){
18159 this.setActiveBullet(this.indexOfPanel(pan));
18162 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18164 this.transition = true;
18165 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18166 var lr = dir == 'next' ? 'left' : 'right';
18167 pan.el.addClass(dir); // or prev
18168 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18169 cur.el.addClass(lr); // or right
18170 pan.el.addClass(lr);
18173 cur.el.on('transitionend', function() {
18174 Roo.log("trans end?");
18176 pan.el.removeClass([lr,dir]);
18177 pan.setActive(true);
18179 cur.el.removeClass([lr]);
18180 cur.setActive(false);
18182 _this.transition = false;
18184 }, this, { single: true } );
18189 cur.setActive(false);
18190 pan.setActive(true);
18195 showPanelNext : function()
18197 var i = this.indexOfPanel(this.getActivePanel());
18199 if (i >= this.tabs.length - 1 && !this.autoslide) {
18203 if (i >= this.tabs.length - 1 && this.autoslide) {
18207 this.showPanel(this.tabs[i+1]);
18210 showPanelPrev : function()
18212 var i = this.indexOfPanel(this.getActivePanel());
18214 if (i < 1 && !this.autoslide) {
18218 if (i < 1 && this.autoslide) {
18219 i = this.tabs.length;
18222 this.showPanel(this.tabs[i-1]);
18226 addBullet: function()
18228 if(!this.bullets || Roo.isTouch){
18231 var ctr = this.el.select('.carousel-bullets',true).first();
18232 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18233 var bullet = ctr.createChild({
18234 cls : 'bullet bullet-' + i
18235 },ctr.dom.lastChild);
18240 bullet.on('click', (function(e, el, o, ii, t){
18242 e.preventDefault();
18244 this.showPanel(ii);
18246 if(this.autoslide && this.slideFn){
18247 clearInterval(this.slideFn);
18248 this.slideFn = window.setInterval(function() {
18249 _this.showPanelNext();
18253 }).createDelegate(this, [i, bullet], true));
18258 setActiveBullet : function(i)
18264 Roo.each(this.el.select('.bullet', true).elements, function(el){
18265 el.removeClass('selected');
18268 var bullet = this.el.select('.bullet-' + i, true).first();
18274 bullet.addClass('selected');
18285 Roo.apply(Roo.bootstrap.TabGroup, {
18289 * register a Navigation Group
18290 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18292 register : function(navgrp)
18294 this.groups[navgrp.navId] = navgrp;
18298 * fetch a Navigation Group based on the navigation ID
18299 * if one does not exist , it will get created.
18300 * @param {string} the navgroup to add
18301 * @returns {Roo.bootstrap.NavGroup} the navgroup
18303 get: function(navId) {
18304 if (typeof(this.groups[navId]) == 'undefined') {
18305 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18307 return this.groups[navId] ;
18322 * @class Roo.bootstrap.TabPanel
18323 * @extends Roo.bootstrap.Component
18324 * Bootstrap TabPanel class
18325 * @cfg {Boolean} active panel active
18326 * @cfg {String} html panel content
18327 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18328 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18329 * @cfg {String} href click to link..
18333 * Create a new TabPanel
18334 * @param {Object} config The config object
18337 Roo.bootstrap.TabPanel = function(config){
18338 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18342 * Fires when the active status changes
18343 * @param {Roo.bootstrap.TabPanel} this
18344 * @param {Boolean} state the new state
18349 * @event beforedeactivate
18350 * Fires before a tab is de-activated - can be used to do validation on a form.
18351 * @param {Roo.bootstrap.TabPanel} this
18352 * @return {Boolean} false if there is an error
18355 'beforedeactivate': true
18358 this.tabId = this.tabId || Roo.id();
18362 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18370 getAutoCreate : function(){
18373 // item is needed for carousel - not sure if it has any effect otherwise
18374 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18375 html: this.html || ''
18379 cfg.cls += ' active';
18383 cfg.tabId = this.tabId;
18390 initEvents: function()
18392 var p = this.parent();
18394 this.navId = this.navId || p.navId;
18396 if (typeof(this.navId) != 'undefined') {
18397 // not really needed.. but just in case.. parent should be a NavGroup.
18398 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18402 var i = tg.tabs.length - 1;
18404 if(this.active && tg.bullets > 0 && i < tg.bullets){
18405 tg.setActiveBullet(i);
18409 this.el.on('click', this.onClick, this);
18412 this.el.on("touchstart", this.onTouchStart, this);
18413 this.el.on("touchmove", this.onTouchMove, this);
18414 this.el.on("touchend", this.onTouchEnd, this);
18419 onRender : function(ct, position)
18421 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18424 setActive : function(state)
18426 Roo.log("panel - set active " + this.tabId + "=" + state);
18428 this.active = state;
18430 this.el.removeClass('active');
18432 } else if (!this.el.hasClass('active')) {
18433 this.el.addClass('active');
18436 this.fireEvent('changed', this, state);
18439 onClick : function(e)
18441 e.preventDefault();
18443 if(!this.href.length){
18447 window.location.href = this.href;
18456 onTouchStart : function(e)
18458 this.swiping = false;
18460 this.startX = e.browserEvent.touches[0].clientX;
18461 this.startY = e.browserEvent.touches[0].clientY;
18464 onTouchMove : function(e)
18466 this.swiping = true;
18468 this.endX = e.browserEvent.touches[0].clientX;
18469 this.endY = e.browserEvent.touches[0].clientY;
18472 onTouchEnd : function(e)
18479 var tabGroup = this.parent();
18481 if(this.endX > this.startX){ // swiping right
18482 tabGroup.showPanelPrev();
18486 if(this.startX > this.endX){ // swiping left
18487 tabGroup.showPanelNext();
18506 * @class Roo.bootstrap.DateField
18507 * @extends Roo.bootstrap.Input
18508 * Bootstrap DateField class
18509 * @cfg {Number} weekStart default 0
18510 * @cfg {String} viewMode default empty, (months|years)
18511 * @cfg {String} minViewMode default empty, (months|years)
18512 * @cfg {Number} startDate default -Infinity
18513 * @cfg {Number} endDate default Infinity
18514 * @cfg {Boolean} todayHighlight default false
18515 * @cfg {Boolean} todayBtn default false
18516 * @cfg {Boolean} calendarWeeks default false
18517 * @cfg {Object} daysOfWeekDisabled default empty
18518 * @cfg {Boolean} singleMode default false (true | false)
18520 * @cfg {Boolean} keyboardNavigation default true
18521 * @cfg {String} language default en
18524 * Create a new DateField
18525 * @param {Object} config The config object
18528 Roo.bootstrap.DateField = function(config){
18529 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18533 * Fires when this field show.
18534 * @param {Roo.bootstrap.DateField} this
18535 * @param {Mixed} date The date value
18540 * Fires when this field hide.
18541 * @param {Roo.bootstrap.DateField} this
18542 * @param {Mixed} date The date value
18547 * Fires when select a date.
18548 * @param {Roo.bootstrap.DateField} this
18549 * @param {Mixed} date The date value
18553 * @event beforeselect
18554 * Fires when before select a date.
18555 * @param {Roo.bootstrap.DateField} this
18556 * @param {Mixed} date The date value
18558 beforeselect : true
18562 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18565 * @cfg {String} format
18566 * The default date format string which can be overriden for localization support. The format must be
18567 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18571 * @cfg {String} altFormats
18572 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18573 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18575 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18583 todayHighlight : false,
18589 keyboardNavigation: true,
18591 calendarWeeks: false,
18593 startDate: -Infinity,
18597 daysOfWeekDisabled: [],
18601 singleMode : false,
18603 UTCDate: function()
18605 return new Date(Date.UTC.apply(Date, arguments));
18608 UTCToday: function()
18610 var today = new Date();
18611 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18614 getDate: function() {
18615 var d = this.getUTCDate();
18616 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18619 getUTCDate: function() {
18623 setDate: function(d) {
18624 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18627 setUTCDate: function(d) {
18629 this.setValue(this.formatDate(this.date));
18632 onRender: function(ct, position)
18635 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18637 this.language = this.language || 'en';
18638 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18639 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18641 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18642 this.format = this.format || 'm/d/y';
18643 this.isInline = false;
18644 this.isInput = true;
18645 this.component = this.el.select('.add-on', true).first() || false;
18646 this.component = (this.component && this.component.length === 0) ? false : this.component;
18647 this.hasInput = this.component && this.inputEl().length;
18649 if (typeof(this.minViewMode === 'string')) {
18650 switch (this.minViewMode) {
18652 this.minViewMode = 1;
18655 this.minViewMode = 2;
18658 this.minViewMode = 0;
18663 if (typeof(this.viewMode === 'string')) {
18664 switch (this.viewMode) {
18677 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18679 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18681 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18683 this.picker().on('mousedown', this.onMousedown, this);
18684 this.picker().on('click', this.onClick, this);
18686 this.picker().addClass('datepicker-dropdown');
18688 this.startViewMode = this.viewMode;
18690 if(this.singleMode){
18691 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18692 v.setVisibilityMode(Roo.Element.DISPLAY);
18696 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18697 v.setStyle('width', '189px');
18701 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18702 if(!this.calendarWeeks){
18707 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18708 v.attr('colspan', function(i, val){
18709 return parseInt(val) + 1;
18714 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18716 this.setStartDate(this.startDate);
18717 this.setEndDate(this.endDate);
18719 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18726 if(this.isInline) {
18731 picker : function()
18733 return this.pickerEl;
18734 // return this.el.select('.datepicker', true).first();
18737 fillDow: function()
18739 var dowCnt = this.weekStart;
18748 if(this.calendarWeeks){
18756 while (dowCnt < this.weekStart + 7) {
18760 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18764 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18767 fillMonths: function()
18770 var months = this.picker().select('>.datepicker-months td', true).first();
18772 months.dom.innerHTML = '';
18778 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18781 months.createChild(month);
18788 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;
18790 if (this.date < this.startDate) {
18791 this.viewDate = new Date(this.startDate);
18792 } else if (this.date > this.endDate) {
18793 this.viewDate = new Date(this.endDate);
18795 this.viewDate = new Date(this.date);
18803 var d = new Date(this.viewDate),
18804 year = d.getUTCFullYear(),
18805 month = d.getUTCMonth(),
18806 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18807 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18808 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18809 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18810 currentDate = this.date && this.date.valueOf(),
18811 today = this.UTCToday();
18813 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18815 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18817 // this.picker.select('>tfoot th.today').
18818 // .text(dates[this.language].today)
18819 // .toggle(this.todayBtn !== false);
18821 this.updateNavArrows();
18824 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18826 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18828 prevMonth.setUTCDate(day);
18830 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18832 var nextMonth = new Date(prevMonth);
18834 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18836 nextMonth = nextMonth.valueOf();
18838 var fillMonths = false;
18840 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18842 while(prevMonth.valueOf() <= nextMonth) {
18845 if (prevMonth.getUTCDay() === this.weekStart) {
18847 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18855 if(this.calendarWeeks){
18856 // ISO 8601: First week contains first thursday.
18857 // ISO also states week starts on Monday, but we can be more abstract here.
18859 // Start of current week: based on weekstart/current date
18860 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18861 // Thursday of this week
18862 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18863 // First Thursday of year, year from thursday
18864 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18865 // Calendar week: ms between thursdays, div ms per day, div 7 days
18866 calWeek = (th - yth) / 864e5 / 7 + 1;
18868 fillMonths.cn.push({
18876 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18878 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18881 if (this.todayHighlight &&
18882 prevMonth.getUTCFullYear() == today.getFullYear() &&
18883 prevMonth.getUTCMonth() == today.getMonth() &&
18884 prevMonth.getUTCDate() == today.getDate()) {
18885 clsName += ' today';
18888 if (currentDate && prevMonth.valueOf() === currentDate) {
18889 clsName += ' active';
18892 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18893 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18894 clsName += ' disabled';
18897 fillMonths.cn.push({
18899 cls: 'day ' + clsName,
18900 html: prevMonth.getDate()
18903 prevMonth.setDate(prevMonth.getDate()+1);
18906 var currentYear = this.date && this.date.getUTCFullYear();
18907 var currentMonth = this.date && this.date.getUTCMonth();
18909 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18911 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18912 v.removeClass('active');
18914 if(currentYear === year && k === currentMonth){
18915 v.addClass('active');
18918 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18919 v.addClass('disabled');
18925 year = parseInt(year/10, 10) * 10;
18927 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18929 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18932 for (var i = -1; i < 11; i++) {
18933 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18935 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18943 showMode: function(dir)
18946 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18949 Roo.each(this.picker().select('>div',true).elements, function(v){
18951 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18954 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18959 if(this.isInline) {
18963 this.picker().removeClass(['bottom', 'top']);
18965 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18967 * place to the top of element!
18971 this.picker().addClass('top');
18972 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18977 this.picker().addClass('bottom');
18979 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18982 parseDate : function(value)
18984 if(!value || value instanceof Date){
18987 var v = Date.parseDate(value, this.format);
18988 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18989 v = Date.parseDate(value, 'Y-m-d');
18991 if(!v && this.altFormats){
18992 if(!this.altFormatsArray){
18993 this.altFormatsArray = this.altFormats.split("|");
18995 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18996 v = Date.parseDate(value, this.altFormatsArray[i]);
19002 formatDate : function(date, fmt)
19004 return (!date || !(date instanceof Date)) ?
19005 date : date.dateFormat(fmt || this.format);
19008 onFocus : function()
19010 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19014 onBlur : function()
19016 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19018 var d = this.inputEl().getValue();
19025 showPopup : function()
19027 this.picker().show();
19031 this.fireEvent('showpopup', this, this.date);
19034 hidePopup : function()
19036 if(this.isInline) {
19039 this.picker().hide();
19040 this.viewMode = this.startViewMode;
19043 this.fireEvent('hidepopup', this, this.date);
19047 onMousedown: function(e)
19049 e.stopPropagation();
19050 e.preventDefault();
19055 Roo.bootstrap.DateField.superclass.keyup.call(this);
19059 setValue: function(v)
19061 if(this.fireEvent('beforeselect', this, v) !== false){
19062 var d = new Date(this.parseDate(v) ).clearTime();
19064 if(isNaN(d.getTime())){
19065 this.date = this.viewDate = '';
19066 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19070 v = this.formatDate(d);
19072 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19074 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19078 this.fireEvent('select', this, this.date);
19082 getValue: function()
19084 return this.formatDate(this.date);
19087 fireKey: function(e)
19089 if (!this.picker().isVisible()){
19090 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19096 var dateChanged = false,
19098 newDate, newViewDate;
19103 e.preventDefault();
19107 if (!this.keyboardNavigation) {
19110 dir = e.keyCode == 37 ? -1 : 1;
19113 newDate = this.moveYear(this.date, dir);
19114 newViewDate = this.moveYear(this.viewDate, dir);
19115 } else if (e.shiftKey){
19116 newDate = this.moveMonth(this.date, dir);
19117 newViewDate = this.moveMonth(this.viewDate, dir);
19119 newDate = new Date(this.date);
19120 newDate.setUTCDate(this.date.getUTCDate() + dir);
19121 newViewDate = new Date(this.viewDate);
19122 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19124 if (this.dateWithinRange(newDate)){
19125 this.date = newDate;
19126 this.viewDate = newViewDate;
19127 this.setValue(this.formatDate(this.date));
19129 e.preventDefault();
19130 dateChanged = true;
19135 if (!this.keyboardNavigation) {
19138 dir = e.keyCode == 38 ? -1 : 1;
19140 newDate = this.moveYear(this.date, dir);
19141 newViewDate = this.moveYear(this.viewDate, dir);
19142 } else if (e.shiftKey){
19143 newDate = this.moveMonth(this.date, dir);
19144 newViewDate = this.moveMonth(this.viewDate, dir);
19146 newDate = new Date(this.date);
19147 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19148 newViewDate = new Date(this.viewDate);
19149 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19151 if (this.dateWithinRange(newDate)){
19152 this.date = newDate;
19153 this.viewDate = newViewDate;
19154 this.setValue(this.formatDate(this.date));
19156 e.preventDefault();
19157 dateChanged = true;
19161 this.setValue(this.formatDate(this.date));
19163 e.preventDefault();
19166 this.setValue(this.formatDate(this.date));
19180 onClick: function(e)
19182 e.stopPropagation();
19183 e.preventDefault();
19185 var target = e.getTarget();
19187 if(target.nodeName.toLowerCase() === 'i'){
19188 target = Roo.get(target).dom.parentNode;
19191 var nodeName = target.nodeName;
19192 var className = target.className;
19193 var html = target.innerHTML;
19194 //Roo.log(nodeName);
19196 switch(nodeName.toLowerCase()) {
19198 switch(className) {
19204 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19205 switch(this.viewMode){
19207 this.viewDate = this.moveMonth(this.viewDate, dir);
19211 this.viewDate = this.moveYear(this.viewDate, dir);
19217 var date = new Date();
19218 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19220 this.setValue(this.formatDate(this.date));
19227 if (className.indexOf('disabled') < 0) {
19228 this.viewDate.setUTCDate(1);
19229 if (className.indexOf('month') > -1) {
19230 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19232 var year = parseInt(html, 10) || 0;
19233 this.viewDate.setUTCFullYear(year);
19237 if(this.singleMode){
19238 this.setValue(this.formatDate(this.viewDate));
19249 //Roo.log(className);
19250 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19251 var day = parseInt(html, 10) || 1;
19252 var year = this.viewDate.getUTCFullYear(),
19253 month = this.viewDate.getUTCMonth();
19255 if (className.indexOf('old') > -1) {
19262 } else if (className.indexOf('new') > -1) {
19270 //Roo.log([year,month,day]);
19271 this.date = this.UTCDate(year, month, day,0,0,0,0);
19272 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19274 //Roo.log(this.formatDate(this.date));
19275 this.setValue(this.formatDate(this.date));
19282 setStartDate: function(startDate)
19284 this.startDate = startDate || -Infinity;
19285 if (this.startDate !== -Infinity) {
19286 this.startDate = this.parseDate(this.startDate);
19289 this.updateNavArrows();
19292 setEndDate: function(endDate)
19294 this.endDate = endDate || Infinity;
19295 if (this.endDate !== Infinity) {
19296 this.endDate = this.parseDate(this.endDate);
19299 this.updateNavArrows();
19302 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19304 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19305 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19306 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19308 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19309 return parseInt(d, 10);
19312 this.updateNavArrows();
19315 updateNavArrows: function()
19317 if(this.singleMode){
19321 var d = new Date(this.viewDate),
19322 year = d.getUTCFullYear(),
19323 month = d.getUTCMonth();
19325 Roo.each(this.picker().select('.prev', true).elements, function(v){
19327 switch (this.viewMode) {
19330 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19336 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19343 Roo.each(this.picker().select('.next', true).elements, function(v){
19345 switch (this.viewMode) {
19348 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19354 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19362 moveMonth: function(date, dir)
19367 var new_date = new Date(date.valueOf()),
19368 day = new_date.getUTCDate(),
19369 month = new_date.getUTCMonth(),
19370 mag = Math.abs(dir),
19372 dir = dir > 0 ? 1 : -1;
19375 // If going back one month, make sure month is not current month
19376 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19378 return new_date.getUTCMonth() == month;
19380 // If going forward one month, make sure month is as expected
19381 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19383 return new_date.getUTCMonth() != new_month;
19385 new_month = month + dir;
19386 new_date.setUTCMonth(new_month);
19387 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19388 if (new_month < 0 || new_month > 11) {
19389 new_month = (new_month + 12) % 12;
19392 // For magnitudes >1, move one month at a time...
19393 for (var i=0; i<mag; i++) {
19394 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19395 new_date = this.moveMonth(new_date, dir);
19397 // ...then reset the day, keeping it in the new month
19398 new_month = new_date.getUTCMonth();
19399 new_date.setUTCDate(day);
19401 return new_month != new_date.getUTCMonth();
19404 // Common date-resetting loop -- if date is beyond end of month, make it
19407 new_date.setUTCDate(--day);
19408 new_date.setUTCMonth(new_month);
19413 moveYear: function(date, dir)
19415 return this.moveMonth(date, dir*12);
19418 dateWithinRange: function(date)
19420 return date >= this.startDate && date <= this.endDate;
19426 this.picker().remove();
19429 validateValue : function(value)
19431 if(this.getVisibilityEl().hasClass('hidden')){
19435 if(value.length < 1) {
19436 if(this.allowBlank){
19442 if(value.length < this.minLength){
19445 if(value.length > this.maxLength){
19449 var vt = Roo.form.VTypes;
19450 if(!vt[this.vtype](value, this)){
19454 if(typeof this.validator == "function"){
19455 var msg = this.validator(value);
19461 if(this.regex && !this.regex.test(value)){
19465 if(typeof(this.parseDate(value)) == 'undefined'){
19469 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19473 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19483 this.date = this.viewDate = '';
19485 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19490 Roo.apply(Roo.bootstrap.DateField, {
19501 html: '<i class="fa fa-arrow-left"/>'
19511 html: '<i class="fa fa-arrow-right"/>'
19553 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19554 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19555 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19556 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19557 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19570 navFnc: 'FullYear',
19575 navFnc: 'FullYear',
19580 Roo.apply(Roo.bootstrap.DateField, {
19584 cls: 'datepicker dropdown-menu roo-dynamic',
19588 cls: 'datepicker-days',
19592 cls: 'table-condensed',
19594 Roo.bootstrap.DateField.head,
19598 Roo.bootstrap.DateField.footer
19605 cls: 'datepicker-months',
19609 cls: 'table-condensed',
19611 Roo.bootstrap.DateField.head,
19612 Roo.bootstrap.DateField.content,
19613 Roo.bootstrap.DateField.footer
19620 cls: 'datepicker-years',
19624 cls: 'table-condensed',
19626 Roo.bootstrap.DateField.head,
19627 Roo.bootstrap.DateField.content,
19628 Roo.bootstrap.DateField.footer
19647 * @class Roo.bootstrap.TimeField
19648 * @extends Roo.bootstrap.Input
19649 * Bootstrap DateField class
19653 * Create a new TimeField
19654 * @param {Object} config The config object
19657 Roo.bootstrap.TimeField = function(config){
19658 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19662 * Fires when this field show.
19663 * @param {Roo.bootstrap.DateField} thisthis
19664 * @param {Mixed} date The date value
19669 * Fires when this field hide.
19670 * @param {Roo.bootstrap.DateField} this
19671 * @param {Mixed} date The date value
19676 * Fires when select a date.
19677 * @param {Roo.bootstrap.DateField} this
19678 * @param {Mixed} date The date value
19684 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19687 * @cfg {String} format
19688 * The default time format string which can be overriden for localization support. The format must be
19689 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19693 onRender: function(ct, position)
19696 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19698 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19700 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19702 this.pop = this.picker().select('>.datepicker-time',true).first();
19703 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19705 this.picker().on('mousedown', this.onMousedown, this);
19706 this.picker().on('click', this.onClick, this);
19708 this.picker().addClass('datepicker-dropdown');
19713 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19714 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19715 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19716 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19717 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19718 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19722 fireKey: function(e){
19723 if (!this.picker().isVisible()){
19724 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19730 e.preventDefault();
19738 this.onTogglePeriod();
19741 this.onIncrementMinutes();
19744 this.onDecrementMinutes();
19753 onClick: function(e) {
19754 e.stopPropagation();
19755 e.preventDefault();
19758 picker : function()
19760 return this.el.select('.datepicker', true).first();
19763 fillTime: function()
19765 var time = this.pop.select('tbody', true).first();
19767 time.dom.innerHTML = '';
19782 cls: 'hours-up glyphicon glyphicon-chevron-up'
19802 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19823 cls: 'timepicker-hour',
19838 cls: 'timepicker-minute',
19853 cls: 'btn btn-primary period',
19875 cls: 'hours-down glyphicon glyphicon-chevron-down'
19895 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19913 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19920 var hours = this.time.getHours();
19921 var minutes = this.time.getMinutes();
19934 hours = hours - 12;
19938 hours = '0' + hours;
19942 minutes = '0' + minutes;
19945 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19946 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19947 this.pop.select('button', true).first().dom.innerHTML = period;
19953 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19955 var cls = ['bottom'];
19957 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19964 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19969 this.picker().addClass(cls.join('-'));
19973 Roo.each(cls, function(c){
19975 _this.picker().setTop(_this.inputEl().getHeight());
19979 _this.picker().setTop(0 - _this.picker().getHeight());
19984 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19988 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19995 onFocus : function()
19997 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20001 onBlur : function()
20003 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20009 this.picker().show();
20014 this.fireEvent('show', this, this.date);
20019 this.picker().hide();
20022 this.fireEvent('hide', this, this.date);
20025 setTime : function()
20028 this.setValue(this.time.format(this.format));
20030 this.fireEvent('select', this, this.date);
20035 onMousedown: function(e){
20036 e.stopPropagation();
20037 e.preventDefault();
20040 onIncrementHours: function()
20042 Roo.log('onIncrementHours');
20043 this.time = this.time.add(Date.HOUR, 1);
20048 onDecrementHours: function()
20050 Roo.log('onDecrementHours');
20051 this.time = this.time.add(Date.HOUR, -1);
20055 onIncrementMinutes: function()
20057 Roo.log('onIncrementMinutes');
20058 this.time = this.time.add(Date.MINUTE, 1);
20062 onDecrementMinutes: function()
20064 Roo.log('onDecrementMinutes');
20065 this.time = this.time.add(Date.MINUTE, -1);
20069 onTogglePeriod: function()
20071 Roo.log('onTogglePeriod');
20072 this.time = this.time.add(Date.HOUR, 12);
20079 Roo.apply(Roo.bootstrap.TimeField, {
20109 cls: 'btn btn-info ok',
20121 Roo.apply(Roo.bootstrap.TimeField, {
20125 cls: 'datepicker dropdown-menu',
20129 cls: 'datepicker-time',
20133 cls: 'table-condensed',
20135 Roo.bootstrap.TimeField.content,
20136 Roo.bootstrap.TimeField.footer
20155 * @class Roo.bootstrap.MonthField
20156 * @extends Roo.bootstrap.Input
20157 * Bootstrap MonthField class
20159 * @cfg {String} language default en
20162 * Create a new MonthField
20163 * @param {Object} config The config object
20166 Roo.bootstrap.MonthField = function(config){
20167 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20172 * Fires when this field show.
20173 * @param {Roo.bootstrap.MonthField} this
20174 * @param {Mixed} date The date value
20179 * Fires when this field hide.
20180 * @param {Roo.bootstrap.MonthField} this
20181 * @param {Mixed} date The date value
20186 * Fires when select a date.
20187 * @param {Roo.bootstrap.MonthField} this
20188 * @param {String} oldvalue The old value
20189 * @param {String} newvalue The new value
20195 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20197 onRender: function(ct, position)
20200 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20202 this.language = this.language || 'en';
20203 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20204 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20206 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20207 this.isInline = false;
20208 this.isInput = true;
20209 this.component = this.el.select('.add-on', true).first() || false;
20210 this.component = (this.component && this.component.length === 0) ? false : this.component;
20211 this.hasInput = this.component && this.inputEL().length;
20213 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20215 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20217 this.picker().on('mousedown', this.onMousedown, this);
20218 this.picker().on('click', this.onClick, this);
20220 this.picker().addClass('datepicker-dropdown');
20222 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20223 v.setStyle('width', '189px');
20230 if(this.isInline) {
20236 setValue: function(v, suppressEvent)
20238 var o = this.getValue();
20240 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20244 if(suppressEvent !== true){
20245 this.fireEvent('select', this, o, v);
20250 getValue: function()
20255 onClick: function(e)
20257 e.stopPropagation();
20258 e.preventDefault();
20260 var target = e.getTarget();
20262 if(target.nodeName.toLowerCase() === 'i'){
20263 target = Roo.get(target).dom.parentNode;
20266 var nodeName = target.nodeName;
20267 var className = target.className;
20268 var html = target.innerHTML;
20270 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20274 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20276 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20282 picker : function()
20284 return this.pickerEl;
20287 fillMonths: function()
20290 var months = this.picker().select('>.datepicker-months td', true).first();
20292 months.dom.innerHTML = '';
20298 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20301 months.createChild(month);
20310 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20311 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20314 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20315 e.removeClass('active');
20317 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20318 e.addClass('active');
20325 if(this.isInline) {
20329 this.picker().removeClass(['bottom', 'top']);
20331 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20333 * place to the top of element!
20337 this.picker().addClass('top');
20338 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20343 this.picker().addClass('bottom');
20345 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20348 onFocus : function()
20350 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20354 onBlur : function()
20356 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20358 var d = this.inputEl().getValue();
20367 this.picker().show();
20368 this.picker().select('>.datepicker-months', true).first().show();
20372 this.fireEvent('show', this, this.date);
20377 if(this.isInline) {
20380 this.picker().hide();
20381 this.fireEvent('hide', this, this.date);
20385 onMousedown: function(e)
20387 e.stopPropagation();
20388 e.preventDefault();
20393 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20397 fireKey: function(e)
20399 if (!this.picker().isVisible()){
20400 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20411 e.preventDefault();
20415 dir = e.keyCode == 37 ? -1 : 1;
20417 this.vIndex = this.vIndex + dir;
20419 if(this.vIndex < 0){
20423 if(this.vIndex > 11){
20427 if(isNaN(this.vIndex)){
20431 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20437 dir = e.keyCode == 38 ? -1 : 1;
20439 this.vIndex = this.vIndex + dir * 4;
20441 if(this.vIndex < 0){
20445 if(this.vIndex > 11){
20449 if(isNaN(this.vIndex)){
20453 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20458 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20459 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20463 e.preventDefault();
20466 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20467 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20483 this.picker().remove();
20488 Roo.apply(Roo.bootstrap.MonthField, {
20507 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20508 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20513 Roo.apply(Roo.bootstrap.MonthField, {
20517 cls: 'datepicker dropdown-menu roo-dynamic',
20521 cls: 'datepicker-months',
20525 cls: 'table-condensed',
20527 Roo.bootstrap.DateField.content
20547 * @class Roo.bootstrap.CheckBox
20548 * @extends Roo.bootstrap.Input
20549 * Bootstrap CheckBox class
20551 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20552 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20553 * @cfg {String} boxLabel The text that appears beside the checkbox
20554 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20555 * @cfg {Boolean} checked initnal the element
20556 * @cfg {Boolean} inline inline the element (default false)
20557 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20558 * @cfg {String} tooltip label tooltip
20561 * Create a new CheckBox
20562 * @param {Object} config The config object
20565 Roo.bootstrap.CheckBox = function(config){
20566 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20571 * Fires when the element is checked or unchecked.
20572 * @param {Roo.bootstrap.CheckBox} this This input
20573 * @param {Boolean} checked The new checked value
20578 * Fires when the element is click.
20579 * @param {Roo.bootstrap.CheckBox} this This input
20586 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20588 inputType: 'checkbox',
20597 getAutoCreate : function()
20599 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20605 cfg.cls = 'form-group ' + this.inputType; //input-group
20608 cfg.cls += ' ' + this.inputType + '-inline';
20614 type : this.inputType,
20615 value : this.inputValue,
20616 cls : 'roo-' + this.inputType, //'form-box',
20617 placeholder : this.placeholder || ''
20621 if(this.inputType != 'radio'){
20625 cls : 'roo-hidden-value',
20626 value : this.checked ? this.inputValue : this.valueOff
20631 if (this.weight) { // Validity check?
20632 cfg.cls += " " + this.inputType + "-" + this.weight;
20635 if (this.disabled) {
20636 input.disabled=true;
20640 input.checked = this.checked;
20645 input.name = this.name;
20647 if(this.inputType != 'radio'){
20648 hidden.name = this.name;
20649 input.name = '_hidden_' + this.name;
20654 input.cls += ' input-' + this.size;
20659 ['xs','sm','md','lg'].map(function(size){
20660 if (settings[size]) {
20661 cfg.cls += ' col-' + size + '-' + settings[size];
20665 var inputblock = input;
20667 if (this.before || this.after) {
20670 cls : 'input-group',
20675 inputblock.cn.push({
20677 cls : 'input-group-addon',
20682 inputblock.cn.push(input);
20684 if(this.inputType != 'radio'){
20685 inputblock.cn.push(hidden);
20689 inputblock.cn.push({
20691 cls : 'input-group-addon',
20698 if (align ==='left' && this.fieldLabel.length) {
20699 // Roo.log("left and has label");
20704 cls : 'control-label',
20705 html : this.fieldLabel
20715 if(this.labelWidth > 12){
20716 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20719 if(this.labelWidth < 13 && this.labelmd == 0){
20720 this.labelmd = this.labelWidth;
20723 if(this.labellg > 0){
20724 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20725 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20728 if(this.labelmd > 0){
20729 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20730 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20733 if(this.labelsm > 0){
20734 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20735 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20738 if(this.labelxs > 0){
20739 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20740 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20743 } else if ( this.fieldLabel.length) {
20744 // Roo.log(" label");
20748 tag: this.boxLabel ? 'span' : 'label',
20750 cls: 'control-label box-input-label',
20751 //cls : 'input-group-addon',
20752 html : this.fieldLabel
20761 // Roo.log(" no label && no align");
20762 cfg.cn = [ inputblock ] ;
20768 var boxLabelCfg = {
20770 //'for': id, // box label is handled by onclick - so no for...
20772 html: this.boxLabel
20776 boxLabelCfg.tooltip = this.tooltip;
20779 cfg.cn.push(boxLabelCfg);
20782 if(this.inputType != 'radio'){
20783 cfg.cn.push(hidden);
20791 * return the real input element.
20793 inputEl: function ()
20795 return this.el.select('input.roo-' + this.inputType,true).first();
20797 hiddenEl: function ()
20799 return this.el.select('input.roo-hidden-value',true).first();
20802 labelEl: function()
20804 return this.el.select('label.control-label',true).first();
20806 /* depricated... */
20810 return this.labelEl();
20813 boxLabelEl: function()
20815 return this.el.select('label.box-label',true).first();
20818 initEvents : function()
20820 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20822 this.inputEl().on('click', this.onClick, this);
20824 if (this.boxLabel) {
20825 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20828 this.startValue = this.getValue();
20831 Roo.bootstrap.CheckBox.register(this);
20835 onClick : function(e)
20837 if(this.fireEvent('click', this, e) !== false){
20838 this.setChecked(!this.checked);
20843 setChecked : function(state,suppressEvent)
20845 this.startValue = this.getValue();
20847 if(this.inputType == 'radio'){
20849 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20850 e.dom.checked = false;
20853 this.inputEl().dom.checked = true;
20855 this.inputEl().dom.value = this.inputValue;
20857 if(suppressEvent !== true){
20858 this.fireEvent('check', this, true);
20866 this.checked = state;
20868 this.inputEl().dom.checked = state;
20871 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20873 if(suppressEvent !== true){
20874 this.fireEvent('check', this, state);
20880 getValue : function()
20882 if(this.inputType == 'radio'){
20883 return this.getGroupValue();
20886 return this.hiddenEl().dom.value;
20890 getGroupValue : function()
20892 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20896 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20899 setValue : function(v,suppressEvent)
20901 if(this.inputType == 'radio'){
20902 this.setGroupValue(v, suppressEvent);
20906 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20911 setGroupValue : function(v, suppressEvent)
20913 this.startValue = this.getValue();
20915 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20916 e.dom.checked = false;
20918 if(e.dom.value == v){
20919 e.dom.checked = true;
20923 if(suppressEvent !== true){
20924 this.fireEvent('check', this, true);
20932 validate : function()
20934 if(this.getVisibilityEl().hasClass('hidden')){
20940 (this.inputType == 'radio' && this.validateRadio()) ||
20941 (this.inputType == 'checkbox' && this.validateCheckbox())
20947 this.markInvalid();
20951 validateRadio : function()
20953 if(this.getVisibilityEl().hasClass('hidden')){
20957 if(this.allowBlank){
20963 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20964 if(!e.dom.checked){
20976 validateCheckbox : function()
20979 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20980 //return (this.getValue() == this.inputValue) ? true : false;
20983 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20991 for(var i in group){
20992 if(group[i].el.isVisible(true)){
21000 for(var i in group){
21005 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21012 * Mark this field as valid
21014 markValid : function()
21018 this.fireEvent('valid', this);
21020 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21023 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21030 if(this.inputType == 'radio'){
21031 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21032 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21033 e.findParent('.form-group', false, true).addClass(_this.validClass);
21040 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21041 this.el.findParent('.form-group', false, true).addClass(this.validClass);
21045 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21051 for(var i in group){
21052 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21053 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21058 * Mark this field as invalid
21059 * @param {String} msg The validation message
21061 markInvalid : function(msg)
21063 if(this.allowBlank){
21069 this.fireEvent('invalid', this, msg);
21071 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21074 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21078 label.markInvalid();
21081 if(this.inputType == 'radio'){
21082 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21083 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21084 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21091 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21092 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21096 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21102 for(var i in group){
21103 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21104 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21109 clearInvalid : function()
21111 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21113 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21115 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21117 if (label && label.iconEl) {
21118 label.iconEl.removeClass(label.validClass);
21119 label.iconEl.removeClass(label.invalidClass);
21123 disable : function()
21125 if(this.inputType != 'radio'){
21126 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21133 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21134 _this.getActionEl().addClass(this.disabledClass);
21135 e.dom.disabled = true;
21139 this.disabled = true;
21140 this.fireEvent("disable", this);
21144 enable : function()
21146 if(this.inputType != 'radio'){
21147 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21154 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21155 _this.getActionEl().removeClass(this.disabledClass);
21156 e.dom.disabled = false;
21160 this.disabled = false;
21161 this.fireEvent("enable", this);
21165 setBoxLabel : function(v)
21170 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21176 Roo.apply(Roo.bootstrap.CheckBox, {
21181 * register a CheckBox Group
21182 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21184 register : function(checkbox)
21186 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21187 this.groups[checkbox.groupId] = {};
21190 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21194 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21198 * fetch a CheckBox Group based on the group ID
21199 * @param {string} the group ID
21200 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21202 get: function(groupId) {
21203 if (typeof(this.groups[groupId]) == 'undefined') {
21207 return this.groups[groupId] ;
21220 * @class Roo.bootstrap.Radio
21221 * @extends Roo.bootstrap.Component
21222 * Bootstrap Radio class
21223 * @cfg {String} boxLabel - the label associated
21224 * @cfg {String} value - the value of radio
21227 * Create a new Radio
21228 * @param {Object} config The config object
21230 Roo.bootstrap.Radio = function(config){
21231 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21235 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21241 getAutoCreate : function()
21245 cls : 'form-group radio',
21250 html : this.boxLabel
21258 initEvents : function()
21260 this.parent().register(this);
21262 this.el.on('click', this.onClick, this);
21266 onClick : function(e)
21268 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21269 this.setChecked(true);
21273 setChecked : function(state, suppressEvent)
21275 this.parent().setValue(this.value, suppressEvent);
21279 setBoxLabel : function(v)
21284 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21299 * @class Roo.bootstrap.SecurePass
21300 * @extends Roo.bootstrap.Input
21301 * Bootstrap SecurePass class
21305 * Create a new SecurePass
21306 * @param {Object} config The config object
21309 Roo.bootstrap.SecurePass = function (config) {
21310 // these go here, so the translation tool can replace them..
21312 PwdEmpty: "Please type a password, and then retype it to confirm.",
21313 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21314 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21315 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21316 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21317 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21318 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21319 TooWeak: "Your password is Too Weak."
21321 this.meterLabel = "Password strength:";
21322 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21323 this.meterClass = [
21324 "roo-password-meter-tooweak",
21325 "roo-password-meter-weak",
21326 "roo-password-meter-medium",
21327 "roo-password-meter-strong",
21328 "roo-password-meter-grey"
21333 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21336 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21338 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21340 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21341 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21342 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21343 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21344 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21345 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21346 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21356 * @cfg {String/Object} Label for the strength meter (defaults to
21357 * 'Password strength:')
21362 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21363 * ['Weak', 'Medium', 'Strong'])
21366 pwdStrengths: false,
21379 initEvents: function ()
21381 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21383 if (this.el.is('input[type=password]') && Roo.isSafari) {
21384 this.el.on('keydown', this.SafariOnKeyDown, this);
21387 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21390 onRender: function (ct, position)
21392 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21393 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21394 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21396 this.trigger.createChild({
21401 cls: 'roo-password-meter-grey col-xs-12',
21404 //width: this.meterWidth + 'px'
21408 cls: 'roo-password-meter-text'
21414 if (this.hideTrigger) {
21415 this.trigger.setDisplayed(false);
21417 this.setSize(this.width || '', this.height || '');
21420 onDestroy: function ()
21422 if (this.trigger) {
21423 this.trigger.removeAllListeners();
21424 this.trigger.remove();
21427 this.wrap.remove();
21429 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21432 checkStrength: function ()
21434 var pwd = this.inputEl().getValue();
21435 if (pwd == this._lastPwd) {
21440 if (this.ClientSideStrongPassword(pwd)) {
21442 } else if (this.ClientSideMediumPassword(pwd)) {
21444 } else if (this.ClientSideWeakPassword(pwd)) {
21450 Roo.log('strength1: ' + strength);
21452 //var pm = this.trigger.child('div/div/div').dom;
21453 var pm = this.trigger.child('div/div');
21454 pm.removeClass(this.meterClass);
21455 pm.addClass(this.meterClass[strength]);
21458 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21460 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21462 this._lastPwd = pwd;
21466 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21468 this._lastPwd = '';
21470 var pm = this.trigger.child('div/div');
21471 pm.removeClass(this.meterClass);
21472 pm.addClass('roo-password-meter-grey');
21475 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21478 this.inputEl().dom.type='password';
21481 validateValue: function (value)
21484 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21487 if (value.length == 0) {
21488 if (this.allowBlank) {
21489 this.clearInvalid();
21493 this.markInvalid(this.errors.PwdEmpty);
21494 this.errorMsg = this.errors.PwdEmpty;
21502 if ('[\x21-\x7e]*'.match(value)) {
21503 this.markInvalid(this.errors.PwdBadChar);
21504 this.errorMsg = this.errors.PwdBadChar;
21507 if (value.length < 6) {
21508 this.markInvalid(this.errors.PwdShort);
21509 this.errorMsg = this.errors.PwdShort;
21512 if (value.length > 16) {
21513 this.markInvalid(this.errors.PwdLong);
21514 this.errorMsg = this.errors.PwdLong;
21518 if (this.ClientSideStrongPassword(value)) {
21520 } else if (this.ClientSideMediumPassword(value)) {
21522 } else if (this.ClientSideWeakPassword(value)) {
21529 if (strength < 2) {
21530 //this.markInvalid(this.errors.TooWeak);
21531 this.errorMsg = this.errors.TooWeak;
21536 console.log('strength2: ' + strength);
21538 //var pm = this.trigger.child('div/div/div').dom;
21540 var pm = this.trigger.child('div/div');
21541 pm.removeClass(this.meterClass);
21542 pm.addClass(this.meterClass[strength]);
21544 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21546 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21548 this.errorMsg = '';
21552 CharacterSetChecks: function (type)
21555 this.fResult = false;
21558 isctype: function (character, type)
21561 case this.kCapitalLetter:
21562 if (character >= 'A' && character <= 'Z') {
21567 case this.kSmallLetter:
21568 if (character >= 'a' && character <= 'z') {
21574 if (character >= '0' && character <= '9') {
21579 case this.kPunctuation:
21580 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21591 IsLongEnough: function (pwd, size)
21593 return !(pwd == null || isNaN(size) || pwd.length < size);
21596 SpansEnoughCharacterSets: function (word, nb)
21598 if (!this.IsLongEnough(word, nb))
21603 var characterSetChecks = new Array(
21604 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21605 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21608 for (var index = 0; index < word.length; ++index) {
21609 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21610 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21611 characterSetChecks[nCharSet].fResult = true;
21618 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21619 if (characterSetChecks[nCharSet].fResult) {
21624 if (nCharSets < nb) {
21630 ClientSideStrongPassword: function (pwd)
21632 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21635 ClientSideMediumPassword: function (pwd)
21637 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21640 ClientSideWeakPassword: function (pwd)
21642 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21645 })//<script type="text/javascript">
21648 * Based Ext JS Library 1.1.1
21649 * Copyright(c) 2006-2007, Ext JS, LLC.
21655 * @class Roo.HtmlEditorCore
21656 * @extends Roo.Component
21657 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21659 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21662 Roo.HtmlEditorCore = function(config){
21665 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21670 * @event initialize
21671 * Fires when the editor is fully initialized (including the iframe)
21672 * @param {Roo.HtmlEditorCore} this
21677 * Fires when the editor is first receives the focus. Any insertion must wait
21678 * until after this event.
21679 * @param {Roo.HtmlEditorCore} this
21683 * @event beforesync
21684 * Fires before the textarea is updated with content from the editor iframe. Return false
21685 * to cancel the sync.
21686 * @param {Roo.HtmlEditorCore} this
21687 * @param {String} html
21691 * @event beforepush
21692 * Fires before the iframe editor is updated with content from the textarea. Return false
21693 * to cancel the push.
21694 * @param {Roo.HtmlEditorCore} this
21695 * @param {String} html
21700 * Fires when the textarea is updated with content from the editor iframe.
21701 * @param {Roo.HtmlEditorCore} this
21702 * @param {String} html
21707 * Fires when the iframe editor is updated with content from the textarea.
21708 * @param {Roo.HtmlEditorCore} this
21709 * @param {String} html
21714 * @event editorevent
21715 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21716 * @param {Roo.HtmlEditorCore} this
21722 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21724 // defaults : white / black...
21725 this.applyBlacklists();
21732 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21736 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21742 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21747 * @cfg {Number} height (in pixels)
21751 * @cfg {Number} width (in pixels)
21756 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21759 stylesheets: false,
21764 // private properties
21765 validationEvent : false,
21767 initialized : false,
21769 sourceEditMode : false,
21770 onFocus : Roo.emptyFn,
21772 hideMode:'offsets',
21776 // blacklist + whitelisted elements..
21783 * Protected method that will not generally be called directly. It
21784 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21785 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21787 getDocMarkup : function(){
21791 // inherit styels from page...??
21792 if (this.stylesheets === false) {
21794 Roo.get(document.head).select('style').each(function(node) {
21795 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21798 Roo.get(document.head).select('link').each(function(node) {
21799 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21802 } else if (!this.stylesheets.length) {
21804 st = '<style type="text/css">' +
21805 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21808 st = '<style type="text/css">' +
21813 st += '<style type="text/css">' +
21814 'IMG { cursor: pointer } ' +
21817 var cls = 'roo-htmleditor-body';
21819 if(this.bodyCls.length){
21820 cls += ' ' + this.bodyCls;
21823 return '<html><head>' + st +
21824 //<style type="text/css">' +
21825 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21827 ' </head><body class="' + cls + '"></body></html>';
21831 onRender : function(ct, position)
21834 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21835 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21838 this.el.dom.style.border = '0 none';
21839 this.el.dom.setAttribute('tabIndex', -1);
21840 this.el.addClass('x-hidden hide');
21844 if(Roo.isIE){ // fix IE 1px bogus margin
21845 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21849 this.frameId = Roo.id();
21853 var iframe = this.owner.wrap.createChild({
21855 cls: 'form-control', // bootstrap..
21857 name: this.frameId,
21858 frameBorder : 'no',
21859 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21864 this.iframe = iframe.dom;
21866 this.assignDocWin();
21868 this.doc.designMode = 'on';
21871 this.doc.write(this.getDocMarkup());
21875 var task = { // must defer to wait for browser to be ready
21877 //console.log("run task?" + this.doc.readyState);
21878 this.assignDocWin();
21879 if(this.doc.body || this.doc.readyState == 'complete'){
21881 this.doc.designMode="on";
21885 Roo.TaskMgr.stop(task);
21886 this.initEditor.defer(10, this);
21893 Roo.TaskMgr.start(task);
21898 onResize : function(w, h)
21900 Roo.log('resize: ' +w + ',' + h );
21901 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21905 if(typeof w == 'number'){
21907 this.iframe.style.width = w + 'px';
21909 if(typeof h == 'number'){
21911 this.iframe.style.height = h + 'px';
21913 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21920 * Toggles the editor between standard and source edit mode.
21921 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21923 toggleSourceEdit : function(sourceEditMode){
21925 this.sourceEditMode = sourceEditMode === true;
21927 if(this.sourceEditMode){
21929 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21932 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21933 //this.iframe.className = '';
21936 //this.setSize(this.owner.wrap.getSize());
21937 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21944 * Protected method that will not generally be called directly. If you need/want
21945 * custom HTML cleanup, this is the method you should override.
21946 * @param {String} html The HTML to be cleaned
21947 * return {String} The cleaned HTML
21949 cleanHtml : function(html){
21950 html = String(html);
21951 if(html.length > 5){
21952 if(Roo.isSafari){ // strip safari nonsense
21953 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21956 if(html == ' '){
21963 * HTML Editor -> Textarea
21964 * Protected method that will not generally be called directly. Syncs the contents
21965 * of the editor iframe with the textarea.
21967 syncValue : function(){
21968 if(this.initialized){
21969 var bd = (this.doc.body || this.doc.documentElement);
21970 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21971 var html = bd.innerHTML;
21973 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21974 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21976 html = '<div style="'+m[0]+'">' + html + '</div>';
21979 html = this.cleanHtml(html);
21980 // fix up the special chars.. normaly like back quotes in word...
21981 // however we do not want to do this with chinese..
21982 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21983 var cc = b.charCodeAt();
21985 (cc >= 0x4E00 && cc < 0xA000 ) ||
21986 (cc >= 0x3400 && cc < 0x4E00 ) ||
21987 (cc >= 0xf900 && cc < 0xfb00 )
21993 if(this.owner.fireEvent('beforesync', this, html) !== false){
21994 this.el.dom.value = html;
21995 this.owner.fireEvent('sync', this, html);
22001 * Protected method that will not generally be called directly. Pushes the value of the textarea
22002 * into the iframe editor.
22004 pushValue : function(){
22005 if(this.initialized){
22006 var v = this.el.dom.value.trim();
22008 // if(v.length < 1){
22012 if(this.owner.fireEvent('beforepush', this, v) !== false){
22013 var d = (this.doc.body || this.doc.documentElement);
22015 this.cleanUpPaste();
22016 this.el.dom.value = d.innerHTML;
22017 this.owner.fireEvent('push', this, v);
22023 deferFocus : function(){
22024 this.focus.defer(10, this);
22028 focus : function(){
22029 if(this.win && !this.sourceEditMode){
22036 assignDocWin: function()
22038 var iframe = this.iframe;
22041 this.doc = iframe.contentWindow.document;
22042 this.win = iframe.contentWindow;
22044 // if (!Roo.get(this.frameId)) {
22047 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22048 // this.win = Roo.get(this.frameId).dom.contentWindow;
22050 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22054 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22055 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22060 initEditor : function(){
22061 //console.log("INIT EDITOR");
22062 this.assignDocWin();
22066 this.doc.designMode="on";
22068 this.doc.write(this.getDocMarkup());
22071 var dbody = (this.doc.body || this.doc.documentElement);
22072 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22073 // this copies styles from the containing element into thsi one..
22074 // not sure why we need all of this..
22075 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22077 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22078 //ss['background-attachment'] = 'fixed'; // w3c
22079 dbody.bgProperties = 'fixed'; // ie
22080 //Roo.DomHelper.applyStyles(dbody, ss);
22081 Roo.EventManager.on(this.doc, {
22082 //'mousedown': this.onEditorEvent,
22083 'mouseup': this.onEditorEvent,
22084 'dblclick': this.onEditorEvent,
22085 'click': this.onEditorEvent,
22086 'keyup': this.onEditorEvent,
22091 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22093 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22094 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22096 this.initialized = true;
22098 this.owner.fireEvent('initialize', this);
22103 onDestroy : function(){
22109 //for (var i =0; i < this.toolbars.length;i++) {
22110 // // fixme - ask toolbars for heights?
22111 // this.toolbars[i].onDestroy();
22114 //this.wrap.dom.innerHTML = '';
22115 //this.wrap.remove();
22120 onFirstFocus : function(){
22122 this.assignDocWin();
22125 this.activated = true;
22128 if(Roo.isGecko){ // prevent silly gecko errors
22130 var s = this.win.getSelection();
22131 if(!s.focusNode || s.focusNode.nodeType != 3){
22132 var r = s.getRangeAt(0);
22133 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22138 this.execCmd('useCSS', true);
22139 this.execCmd('styleWithCSS', false);
22142 this.owner.fireEvent('activate', this);
22146 adjustFont: function(btn){
22147 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22148 //if(Roo.isSafari){ // safari
22151 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22152 if(Roo.isSafari){ // safari
22153 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22154 v = (v < 10) ? 10 : v;
22155 v = (v > 48) ? 48 : v;
22156 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22161 v = Math.max(1, v+adjust);
22163 this.execCmd('FontSize', v );
22166 onEditorEvent : function(e)
22168 this.owner.fireEvent('editorevent', this, e);
22169 // this.updateToolbar();
22170 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22173 insertTag : function(tg)
22175 // could be a bit smarter... -> wrap the current selected tRoo..
22176 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22178 range = this.createRange(this.getSelection());
22179 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22180 wrappingNode.appendChild(range.extractContents());
22181 range.insertNode(wrappingNode);
22188 this.execCmd("formatblock", tg);
22192 insertText : function(txt)
22196 var range = this.createRange();
22197 range.deleteContents();
22198 //alert(Sender.getAttribute('label'));
22200 range.insertNode(this.doc.createTextNode(txt));
22206 * Executes a Midas editor command on the editor document and performs necessary focus and
22207 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22208 * @param {String} cmd The Midas command
22209 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22211 relayCmd : function(cmd, value){
22213 this.execCmd(cmd, value);
22214 this.owner.fireEvent('editorevent', this);
22215 //this.updateToolbar();
22216 this.owner.deferFocus();
22220 * Executes a Midas editor command directly on the editor document.
22221 * For visual commands, you should use {@link #relayCmd} instead.
22222 * <b>This should only be called after the editor is initialized.</b>
22223 * @param {String} cmd The Midas command
22224 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22226 execCmd : function(cmd, value){
22227 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22234 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22236 * @param {String} text | dom node..
22238 insertAtCursor : function(text)
22241 if(!this.activated){
22247 var r = this.doc.selection.createRange();
22258 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22262 // from jquery ui (MIT licenced)
22264 var win = this.win;
22266 if (win.getSelection && win.getSelection().getRangeAt) {
22267 range = win.getSelection().getRangeAt(0);
22268 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22269 range.insertNode(node);
22270 } else if (win.document.selection && win.document.selection.createRange) {
22271 // no firefox support
22272 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22273 win.document.selection.createRange().pasteHTML(txt);
22275 // no firefox support
22276 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22277 this.execCmd('InsertHTML', txt);
22286 mozKeyPress : function(e){
22288 var c = e.getCharCode(), cmd;
22291 c = String.fromCharCode(c).toLowerCase();
22305 this.cleanUpPaste.defer(100, this);
22313 e.preventDefault();
22321 fixKeys : function(){ // load time branching for fastest keydown performance
22323 return function(e){
22324 var k = e.getKey(), r;
22327 r = this.doc.selection.createRange();
22330 r.pasteHTML('    ');
22337 r = this.doc.selection.createRange();
22339 var target = r.parentElement();
22340 if(!target || target.tagName.toLowerCase() != 'li'){
22342 r.pasteHTML('<br />');
22348 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22349 this.cleanUpPaste.defer(100, this);
22355 }else if(Roo.isOpera){
22356 return function(e){
22357 var k = e.getKey();
22361 this.execCmd('InsertHTML','    ');
22364 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22365 this.cleanUpPaste.defer(100, this);
22370 }else if(Roo.isSafari){
22371 return function(e){
22372 var k = e.getKey();
22376 this.execCmd('InsertText','\t');
22380 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22381 this.cleanUpPaste.defer(100, this);
22389 getAllAncestors: function()
22391 var p = this.getSelectedNode();
22394 a.push(p); // push blank onto stack..
22395 p = this.getParentElement();
22399 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22403 a.push(this.doc.body);
22407 lastSelNode : false,
22410 getSelection : function()
22412 this.assignDocWin();
22413 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22416 getSelectedNode: function()
22418 // this may only work on Gecko!!!
22420 // should we cache this!!!!
22425 var range = this.createRange(this.getSelection()).cloneRange();
22428 var parent = range.parentElement();
22430 var testRange = range.duplicate();
22431 testRange.moveToElementText(parent);
22432 if (testRange.inRange(range)) {
22435 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22438 parent = parent.parentElement;
22443 // is ancestor a text element.
22444 var ac = range.commonAncestorContainer;
22445 if (ac.nodeType == 3) {
22446 ac = ac.parentNode;
22449 var ar = ac.childNodes;
22452 var other_nodes = [];
22453 var has_other_nodes = false;
22454 for (var i=0;i<ar.length;i++) {
22455 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22458 // fullly contained node.
22460 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22465 // probably selected..
22466 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22467 other_nodes.push(ar[i]);
22471 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22476 has_other_nodes = true;
22478 if (!nodes.length && other_nodes.length) {
22479 nodes= other_nodes;
22481 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22487 createRange: function(sel)
22489 // this has strange effects when using with
22490 // top toolbar - not sure if it's a great idea.
22491 //this.editor.contentWindow.focus();
22492 if (typeof sel != "undefined") {
22494 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22496 return this.doc.createRange();
22499 return this.doc.createRange();
22502 getParentElement: function()
22505 this.assignDocWin();
22506 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22508 var range = this.createRange(sel);
22511 var p = range.commonAncestorContainer;
22512 while (p.nodeType == 3) { // text node
22523 * Range intersection.. the hard stuff...
22527 * [ -- selected range --- ]
22531 * if end is before start or hits it. fail.
22532 * if start is after end or hits it fail.
22534 * if either hits (but other is outside. - then it's not
22540 // @see http://www.thismuchiknow.co.uk/?p=64.
22541 rangeIntersectsNode : function(range, node)
22543 var nodeRange = node.ownerDocument.createRange();
22545 nodeRange.selectNode(node);
22547 nodeRange.selectNodeContents(node);
22550 var rangeStartRange = range.cloneRange();
22551 rangeStartRange.collapse(true);
22553 var rangeEndRange = range.cloneRange();
22554 rangeEndRange.collapse(false);
22556 var nodeStartRange = nodeRange.cloneRange();
22557 nodeStartRange.collapse(true);
22559 var nodeEndRange = nodeRange.cloneRange();
22560 nodeEndRange.collapse(false);
22562 return rangeStartRange.compareBoundaryPoints(
22563 Range.START_TO_START, nodeEndRange) == -1 &&
22564 rangeEndRange.compareBoundaryPoints(
22565 Range.START_TO_START, nodeStartRange) == 1;
22569 rangeCompareNode : function(range, node)
22571 var nodeRange = node.ownerDocument.createRange();
22573 nodeRange.selectNode(node);
22575 nodeRange.selectNodeContents(node);
22579 range.collapse(true);
22581 nodeRange.collapse(true);
22583 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22584 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22586 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22588 var nodeIsBefore = ss == 1;
22589 var nodeIsAfter = ee == -1;
22591 if (nodeIsBefore && nodeIsAfter) {
22594 if (!nodeIsBefore && nodeIsAfter) {
22595 return 1; //right trailed.
22598 if (nodeIsBefore && !nodeIsAfter) {
22599 return 2; // left trailed.
22605 // private? - in a new class?
22606 cleanUpPaste : function()
22608 // cleans up the whole document..
22609 Roo.log('cleanuppaste');
22611 this.cleanUpChildren(this.doc.body);
22612 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22613 if (clean != this.doc.body.innerHTML) {
22614 this.doc.body.innerHTML = clean;
22619 cleanWordChars : function(input) {// change the chars to hex code
22620 var he = Roo.HtmlEditorCore;
22622 var output = input;
22623 Roo.each(he.swapCodes, function(sw) {
22624 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22626 output = output.replace(swapper, sw[1]);
22633 cleanUpChildren : function (n)
22635 if (!n.childNodes.length) {
22638 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22639 this.cleanUpChild(n.childNodes[i]);
22646 cleanUpChild : function (node)
22649 //console.log(node);
22650 if (node.nodeName == "#text") {
22651 // clean up silly Windows -- stuff?
22654 if (node.nodeName == "#comment") {
22655 node.parentNode.removeChild(node);
22656 // clean up silly Windows -- stuff?
22659 var lcname = node.tagName.toLowerCase();
22660 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22661 // whitelist of tags..
22663 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22665 node.parentNode.removeChild(node);
22670 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22672 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22673 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22675 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22676 // remove_keep_children = true;
22679 if (remove_keep_children) {
22680 this.cleanUpChildren(node);
22681 // inserts everything just before this node...
22682 while (node.childNodes.length) {
22683 var cn = node.childNodes[0];
22684 node.removeChild(cn);
22685 node.parentNode.insertBefore(cn, node);
22687 node.parentNode.removeChild(node);
22691 if (!node.attributes || !node.attributes.length) {
22692 this.cleanUpChildren(node);
22696 function cleanAttr(n,v)
22699 if (v.match(/^\./) || v.match(/^\//)) {
22702 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22705 if (v.match(/^#/)) {
22708 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22709 node.removeAttribute(n);
22713 var cwhite = this.cwhite;
22714 var cblack = this.cblack;
22716 function cleanStyle(n,v)
22718 if (v.match(/expression/)) { //XSS?? should we even bother..
22719 node.removeAttribute(n);
22723 var parts = v.split(/;/);
22726 Roo.each(parts, function(p) {
22727 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22731 var l = p.split(':').shift().replace(/\s+/g,'');
22732 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22734 if ( cwhite.length && cblack.indexOf(l) > -1) {
22735 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22736 //node.removeAttribute(n);
22740 // only allow 'c whitelisted system attributes'
22741 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22742 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22743 //node.removeAttribute(n);
22753 if (clean.length) {
22754 node.setAttribute(n, clean.join(';'));
22756 node.removeAttribute(n);
22762 for (var i = node.attributes.length-1; i > -1 ; i--) {
22763 var a = node.attributes[i];
22766 if (a.name.toLowerCase().substr(0,2)=='on') {
22767 node.removeAttribute(a.name);
22770 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22771 node.removeAttribute(a.name);
22774 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22775 cleanAttr(a.name,a.value); // fixme..
22778 if (a.name == 'style') {
22779 cleanStyle(a.name,a.value);
22782 /// clean up MS crap..
22783 // tecnically this should be a list of valid class'es..
22786 if (a.name == 'class') {
22787 if (a.value.match(/^Mso/)) {
22788 node.className = '';
22791 if (a.value.match(/^body$/)) {
22792 node.className = '';
22803 this.cleanUpChildren(node);
22809 * Clean up MS wordisms...
22811 cleanWord : function(node)
22816 this.cleanWord(this.doc.body);
22819 if (node.nodeName == "#text") {
22820 // clean up silly Windows -- stuff?
22823 if (node.nodeName == "#comment") {
22824 node.parentNode.removeChild(node);
22825 // clean up silly Windows -- stuff?
22829 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22830 node.parentNode.removeChild(node);
22834 // remove - but keep children..
22835 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22836 while (node.childNodes.length) {
22837 var cn = node.childNodes[0];
22838 node.removeChild(cn);
22839 node.parentNode.insertBefore(cn, node);
22841 node.parentNode.removeChild(node);
22842 this.iterateChildren(node, this.cleanWord);
22846 if (node.className.length) {
22848 var cn = node.className.split(/\W+/);
22850 Roo.each(cn, function(cls) {
22851 if (cls.match(/Mso[a-zA-Z]+/)) {
22856 node.className = cna.length ? cna.join(' ') : '';
22858 node.removeAttribute("class");
22862 if (node.hasAttribute("lang")) {
22863 node.removeAttribute("lang");
22866 if (node.hasAttribute("style")) {
22868 var styles = node.getAttribute("style").split(";");
22870 Roo.each(styles, function(s) {
22871 if (!s.match(/:/)) {
22874 var kv = s.split(":");
22875 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22878 // what ever is left... we allow.
22881 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22882 if (!nstyle.length) {
22883 node.removeAttribute('style');
22886 this.iterateChildren(node, this.cleanWord);
22892 * iterateChildren of a Node, calling fn each time, using this as the scole..
22893 * @param {DomNode} node node to iterate children of.
22894 * @param {Function} fn method of this class to call on each item.
22896 iterateChildren : function(node, fn)
22898 if (!node.childNodes.length) {
22901 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22902 fn.call(this, node.childNodes[i])
22908 * cleanTableWidths.
22910 * Quite often pasting from word etc.. results in tables with column and widths.
22911 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22914 cleanTableWidths : function(node)
22919 this.cleanTableWidths(this.doc.body);
22924 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22927 Roo.log(node.tagName);
22928 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22929 this.iterateChildren(node, this.cleanTableWidths);
22932 if (node.hasAttribute('width')) {
22933 node.removeAttribute('width');
22937 if (node.hasAttribute("style")) {
22940 var styles = node.getAttribute("style").split(";");
22942 Roo.each(styles, function(s) {
22943 if (!s.match(/:/)) {
22946 var kv = s.split(":");
22947 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22950 // what ever is left... we allow.
22953 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22954 if (!nstyle.length) {
22955 node.removeAttribute('style');
22959 this.iterateChildren(node, this.cleanTableWidths);
22967 domToHTML : function(currentElement, depth, nopadtext) {
22969 depth = depth || 0;
22970 nopadtext = nopadtext || false;
22972 if (!currentElement) {
22973 return this.domToHTML(this.doc.body);
22976 //Roo.log(currentElement);
22978 var allText = false;
22979 var nodeName = currentElement.nodeName;
22980 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22982 if (nodeName == '#text') {
22984 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22989 if (nodeName != 'BODY') {
22992 // Prints the node tagName, such as <A>, <IMG>, etc
22995 for(i = 0; i < currentElement.attributes.length;i++) {
22997 var aname = currentElement.attributes.item(i).name;
22998 if (!currentElement.attributes.item(i).value.length) {
23001 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23004 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23013 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23016 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23021 // Traverse the tree
23023 var currentElementChild = currentElement.childNodes.item(i);
23024 var allText = true;
23025 var innerHTML = '';
23027 while (currentElementChild) {
23028 // Formatting code (indent the tree so it looks nice on the screen)
23029 var nopad = nopadtext;
23030 if (lastnode == 'SPAN') {
23034 if (currentElementChild.nodeName == '#text') {
23035 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23036 toadd = nopadtext ? toadd : toadd.trim();
23037 if (!nopad && toadd.length > 80) {
23038 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23040 innerHTML += toadd;
23043 currentElementChild = currentElement.childNodes.item(i);
23049 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23051 // Recursively traverse the tree structure of the child node
23052 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23053 lastnode = currentElementChild.nodeName;
23055 currentElementChild=currentElement.childNodes.item(i);
23061 // The remaining code is mostly for formatting the tree
23062 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23067 ret+= "</"+tagName+">";
23073 applyBlacklists : function()
23075 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23076 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23080 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23081 if (b.indexOf(tag) > -1) {
23084 this.white.push(tag);
23088 Roo.each(w, function(tag) {
23089 if (b.indexOf(tag) > -1) {
23092 if (this.white.indexOf(tag) > -1) {
23095 this.white.push(tag);
23100 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23101 if (w.indexOf(tag) > -1) {
23104 this.black.push(tag);
23108 Roo.each(b, function(tag) {
23109 if (w.indexOf(tag) > -1) {
23112 if (this.black.indexOf(tag) > -1) {
23115 this.black.push(tag);
23120 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23121 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23125 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23126 if (b.indexOf(tag) > -1) {
23129 this.cwhite.push(tag);
23133 Roo.each(w, function(tag) {
23134 if (b.indexOf(tag) > -1) {
23137 if (this.cwhite.indexOf(tag) > -1) {
23140 this.cwhite.push(tag);
23145 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23146 if (w.indexOf(tag) > -1) {
23149 this.cblack.push(tag);
23153 Roo.each(b, function(tag) {
23154 if (w.indexOf(tag) > -1) {
23157 if (this.cblack.indexOf(tag) > -1) {
23160 this.cblack.push(tag);
23165 setStylesheets : function(stylesheets)
23167 if(typeof(stylesheets) == 'string'){
23168 Roo.get(this.iframe.contentDocument.head).createChild({
23170 rel : 'stylesheet',
23179 Roo.each(stylesheets, function(s) {
23184 Roo.get(_this.iframe.contentDocument.head).createChild({
23186 rel : 'stylesheet',
23195 removeStylesheets : function()
23199 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23204 setStyle : function(style)
23206 Roo.get(this.iframe.contentDocument.head).createChild({
23215 // hide stuff that is not compatible
23229 * @event specialkey
23233 * @cfg {String} fieldClass @hide
23236 * @cfg {String} focusClass @hide
23239 * @cfg {String} autoCreate @hide
23242 * @cfg {String} inputType @hide
23245 * @cfg {String} invalidClass @hide
23248 * @cfg {String} invalidText @hide
23251 * @cfg {String} msgFx @hide
23254 * @cfg {String} validateOnBlur @hide
23258 Roo.HtmlEditorCore.white = [
23259 'area', 'br', 'img', 'input', 'hr', 'wbr',
23261 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23262 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23263 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23264 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23265 'table', 'ul', 'xmp',
23267 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23270 'dir', 'menu', 'ol', 'ul', 'dl',
23276 Roo.HtmlEditorCore.black = [
23277 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23279 'base', 'basefont', 'bgsound', 'blink', 'body',
23280 'frame', 'frameset', 'head', 'html', 'ilayer',
23281 'iframe', 'layer', 'link', 'meta', 'object',
23282 'script', 'style' ,'title', 'xml' // clean later..
23284 Roo.HtmlEditorCore.clean = [
23285 'script', 'style', 'title', 'xml'
23287 Roo.HtmlEditorCore.remove = [
23292 Roo.HtmlEditorCore.ablack = [
23296 Roo.HtmlEditorCore.aclean = [
23297 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23301 Roo.HtmlEditorCore.pwhite= [
23302 'http', 'https', 'mailto'
23305 // white listed style attributes.
23306 Roo.HtmlEditorCore.cwhite= [
23307 // 'text-align', /// default is to allow most things..
23313 // black listed style attributes.
23314 Roo.HtmlEditorCore.cblack= [
23315 // 'font-size' -- this can be set by the project
23319 Roo.HtmlEditorCore.swapCodes =[
23338 * @class Roo.bootstrap.HtmlEditor
23339 * @extends Roo.bootstrap.TextArea
23340 * Bootstrap HtmlEditor class
23343 * Create a new HtmlEditor
23344 * @param {Object} config The config object
23347 Roo.bootstrap.HtmlEditor = function(config){
23348 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23349 if (!this.toolbars) {
23350 this.toolbars = [];
23353 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23356 * @event initialize
23357 * Fires when the editor is fully initialized (including the iframe)
23358 * @param {HtmlEditor} this
23363 * Fires when the editor is first receives the focus. Any insertion must wait
23364 * until after this event.
23365 * @param {HtmlEditor} this
23369 * @event beforesync
23370 * Fires before the textarea is updated with content from the editor iframe. Return false
23371 * to cancel the sync.
23372 * @param {HtmlEditor} this
23373 * @param {String} html
23377 * @event beforepush
23378 * Fires before the iframe editor is updated with content from the textarea. Return false
23379 * to cancel the push.
23380 * @param {HtmlEditor} this
23381 * @param {String} html
23386 * Fires when the textarea is updated with content from the editor iframe.
23387 * @param {HtmlEditor} this
23388 * @param {String} html
23393 * Fires when the iframe editor is updated with content from the textarea.
23394 * @param {HtmlEditor} this
23395 * @param {String} html
23399 * @event editmodechange
23400 * Fires when the editor switches edit modes
23401 * @param {HtmlEditor} this
23402 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23404 editmodechange: true,
23406 * @event editorevent
23407 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23408 * @param {HtmlEditor} this
23412 * @event firstfocus
23413 * Fires when on first focus - needed by toolbars..
23414 * @param {HtmlEditor} this
23419 * Auto save the htmlEditor value as a file into Events
23420 * @param {HtmlEditor} this
23424 * @event savedpreview
23425 * preview the saved version of htmlEditor
23426 * @param {HtmlEditor} this
23433 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23437 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23442 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23447 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23452 * @cfg {Number} height (in pixels)
23456 * @cfg {Number} width (in pixels)
23461 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23464 stylesheets: false,
23469 // private properties
23470 validationEvent : false,
23472 initialized : false,
23475 onFocus : Roo.emptyFn,
23477 hideMode:'offsets',
23479 tbContainer : false,
23483 toolbarContainer :function() {
23484 return this.wrap.select('.x-html-editor-tb',true).first();
23488 * Protected method that will not generally be called directly. It
23489 * is called when the editor creates its toolbar. Override this method if you need to
23490 * add custom toolbar buttons.
23491 * @param {HtmlEditor} editor
23493 createToolbar : function(){
23494 Roo.log('renewing');
23495 Roo.log("create toolbars");
23497 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23498 this.toolbars[0].render(this.toolbarContainer());
23502 // if (!editor.toolbars || !editor.toolbars.length) {
23503 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23506 // for (var i =0 ; i < editor.toolbars.length;i++) {
23507 // editor.toolbars[i] = Roo.factory(
23508 // typeof(editor.toolbars[i]) == 'string' ?
23509 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23510 // Roo.bootstrap.HtmlEditor);
23511 // editor.toolbars[i].init(editor);
23517 onRender : function(ct, position)
23519 // Roo.log("Call onRender: " + this.xtype);
23521 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23523 this.wrap = this.inputEl().wrap({
23524 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23527 this.editorcore.onRender(ct, position);
23529 if (this.resizable) {
23530 this.resizeEl = new Roo.Resizable(this.wrap, {
23534 minHeight : this.height,
23535 height: this.height,
23536 handles : this.resizable,
23539 resize : function(r, w, h) {
23540 _t.onResize(w,h); // -something
23546 this.createToolbar(this);
23549 if(!this.width && this.resizable){
23550 this.setSize(this.wrap.getSize());
23552 if (this.resizeEl) {
23553 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23554 // should trigger onReize..
23560 onResize : function(w, h)
23562 Roo.log('resize: ' +w + ',' + h );
23563 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23567 if(this.inputEl() ){
23568 if(typeof w == 'number'){
23569 var aw = w - this.wrap.getFrameWidth('lr');
23570 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23573 if(typeof h == 'number'){
23574 var tbh = -11; // fixme it needs to tool bar size!
23575 for (var i =0; i < this.toolbars.length;i++) {
23576 // fixme - ask toolbars for heights?
23577 tbh += this.toolbars[i].el.getHeight();
23578 //if (this.toolbars[i].footer) {
23579 // tbh += this.toolbars[i].footer.el.getHeight();
23587 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23588 ah -= 5; // knock a few pixes off for look..
23589 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23593 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23594 this.editorcore.onResize(ew,eh);
23599 * Toggles the editor between standard and source edit mode.
23600 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23602 toggleSourceEdit : function(sourceEditMode)
23604 this.editorcore.toggleSourceEdit(sourceEditMode);
23606 if(this.editorcore.sourceEditMode){
23607 Roo.log('editor - showing textarea');
23610 // Roo.log(this.syncValue());
23612 this.inputEl().removeClass(['hide', 'x-hidden']);
23613 this.inputEl().dom.removeAttribute('tabIndex');
23614 this.inputEl().focus();
23616 Roo.log('editor - hiding textarea');
23618 // Roo.log(this.pushValue());
23621 this.inputEl().addClass(['hide', 'x-hidden']);
23622 this.inputEl().dom.setAttribute('tabIndex', -1);
23623 //this.deferFocus();
23626 if(this.resizable){
23627 this.setSize(this.wrap.getSize());
23630 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23633 // private (for BoxComponent)
23634 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23636 // private (for BoxComponent)
23637 getResizeEl : function(){
23641 // private (for BoxComponent)
23642 getPositionEl : function(){
23647 initEvents : function(){
23648 this.originalValue = this.getValue();
23652 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23655 // markInvalid : Roo.emptyFn,
23657 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23660 // clearInvalid : Roo.emptyFn,
23662 setValue : function(v){
23663 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23664 this.editorcore.pushValue();
23669 deferFocus : function(){
23670 this.focus.defer(10, this);
23674 focus : function(){
23675 this.editorcore.focus();
23681 onDestroy : function(){
23687 for (var i =0; i < this.toolbars.length;i++) {
23688 // fixme - ask toolbars for heights?
23689 this.toolbars[i].onDestroy();
23692 this.wrap.dom.innerHTML = '';
23693 this.wrap.remove();
23698 onFirstFocus : function(){
23699 //Roo.log("onFirstFocus");
23700 this.editorcore.onFirstFocus();
23701 for (var i =0; i < this.toolbars.length;i++) {
23702 this.toolbars[i].onFirstFocus();
23708 syncValue : function()
23710 this.editorcore.syncValue();
23713 pushValue : function()
23715 this.editorcore.pushValue();
23719 // hide stuff that is not compatible
23733 * @event specialkey
23737 * @cfg {String} fieldClass @hide
23740 * @cfg {String} focusClass @hide
23743 * @cfg {String} autoCreate @hide
23746 * @cfg {String} inputType @hide
23749 * @cfg {String} invalidClass @hide
23752 * @cfg {String} invalidText @hide
23755 * @cfg {String} msgFx @hide
23758 * @cfg {String} validateOnBlur @hide
23767 Roo.namespace('Roo.bootstrap.htmleditor');
23769 * @class Roo.bootstrap.HtmlEditorToolbar1
23774 new Roo.bootstrap.HtmlEditor({
23777 new Roo.bootstrap.HtmlEditorToolbar1({
23778 disable : { fonts: 1 , format: 1, ..., ... , ...],
23784 * @cfg {Object} disable List of elements to disable..
23785 * @cfg {Array} btns List of additional buttons.
23789 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23792 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23795 Roo.apply(this, config);
23797 // default disabled, based on 'good practice'..
23798 this.disable = this.disable || {};
23799 Roo.applyIf(this.disable, {
23802 specialElements : true
23804 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23806 this.editor = config.editor;
23807 this.editorcore = config.editor.editorcore;
23809 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23811 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23812 // dont call parent... till later.
23814 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23819 editorcore : false,
23824 "h1","h2","h3","h4","h5","h6",
23826 "abbr", "acronym", "address", "cite", "samp", "var",
23830 onRender : function(ct, position)
23832 // Roo.log("Call onRender: " + this.xtype);
23834 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23836 this.el.dom.style.marginBottom = '0';
23838 var editorcore = this.editorcore;
23839 var editor= this.editor;
23842 var btn = function(id,cmd , toggle, handler, html){
23844 var event = toggle ? 'toggle' : 'click';
23849 xns: Roo.bootstrap,
23852 enableToggle:toggle !== false,
23854 pressed : toggle ? false : null,
23857 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23858 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23864 // var cb_box = function...
23869 xns: Roo.bootstrap,
23870 glyphicon : 'font',
23874 xns: Roo.bootstrap,
23878 Roo.each(this.formats, function(f) {
23879 style.menu.items.push({
23881 xns: Roo.bootstrap,
23882 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23887 editorcore.insertTag(this.tagname);
23894 children.push(style);
23896 btn('bold',false,true);
23897 btn('italic',false,true);
23898 btn('align-left', 'justifyleft',true);
23899 btn('align-center', 'justifycenter',true);
23900 btn('align-right' , 'justifyright',true);
23901 btn('link', false, false, function(btn) {
23902 //Roo.log("create link?");
23903 var url = prompt(this.createLinkText, this.defaultLinkValue);
23904 if(url && url != 'http:/'+'/'){
23905 this.editorcore.relayCmd('createlink', url);
23908 btn('list','insertunorderedlist',true);
23909 btn('pencil', false,true, function(btn){
23911 this.toggleSourceEdit(btn.pressed);
23914 if (this.editor.btns.length > 0) {
23915 for (var i = 0; i<this.editor.btns.length; i++) {
23916 children.push(this.editor.btns[i]);
23924 xns: Roo.bootstrap,
23929 xns: Roo.bootstrap,
23934 cog.menu.items.push({
23936 xns: Roo.bootstrap,
23937 html : Clean styles,
23942 editorcore.insertTag(this.tagname);
23951 this.xtype = 'NavSimplebar';
23953 for(var i=0;i< children.length;i++) {
23955 this.buttons.add(this.addxtypeChild(children[i]));
23959 editor.on('editorevent', this.updateToolbar, this);
23961 onBtnClick : function(id)
23963 this.editorcore.relayCmd(id);
23964 this.editorcore.focus();
23968 * Protected method that will not generally be called directly. It triggers
23969 * a toolbar update by reading the markup state of the current selection in the editor.
23971 updateToolbar: function(){
23973 if(!this.editorcore.activated){
23974 this.editor.onFirstFocus(); // is this neeed?
23978 var btns = this.buttons;
23979 var doc = this.editorcore.doc;
23980 btns.get('bold').setActive(doc.queryCommandState('bold'));
23981 btns.get('italic').setActive(doc.queryCommandState('italic'));
23982 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23984 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23985 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23986 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23988 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23989 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23992 var ans = this.editorcore.getAllAncestors();
23993 if (this.formatCombo) {
23996 var store = this.formatCombo.store;
23997 this.formatCombo.setValue("");
23998 for (var i =0; i < ans.length;i++) {
23999 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24001 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24009 // hides menus... - so this cant be on a menu...
24010 Roo.bootstrap.MenuMgr.hideAll();
24012 Roo.bootstrap.MenuMgr.hideAll();
24013 //this.editorsyncValue();
24015 onFirstFocus: function() {
24016 this.buttons.each(function(item){
24020 toggleSourceEdit : function(sourceEditMode){
24023 if(sourceEditMode){
24024 Roo.log("disabling buttons");
24025 this.buttons.each( function(item){
24026 if(item.cmd != 'pencil'){
24032 Roo.log("enabling buttons");
24033 if(this.editorcore.initialized){
24034 this.buttons.each( function(item){
24040 Roo.log("calling toggole on editor");
24041 // tell the editor that it's been pressed..
24042 this.editor.toggleSourceEdit(sourceEditMode);
24052 * @class Roo.bootstrap.Table.AbstractSelectionModel
24053 * @extends Roo.util.Observable
24054 * Abstract base class for grid SelectionModels. It provides the interface that should be
24055 * implemented by descendant classes. This class should not be directly instantiated.
24058 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24059 this.locked = false;
24060 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24064 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24065 /** @ignore Called by the grid automatically. Do not call directly. */
24066 init : function(grid){
24072 * Locks the selections.
24075 this.locked = true;
24079 * Unlocks the selections.
24081 unlock : function(){
24082 this.locked = false;
24086 * Returns true if the selections are locked.
24087 * @return {Boolean}
24089 isLocked : function(){
24090 return this.locked;
24094 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24095 * @class Roo.bootstrap.Table.RowSelectionModel
24096 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24097 * It supports multiple selections and keyboard selection/navigation.
24099 * @param {Object} config
24102 Roo.bootstrap.Table.RowSelectionModel = function(config){
24103 Roo.apply(this, config);
24104 this.selections = new Roo.util.MixedCollection(false, function(o){
24109 this.lastActive = false;
24113 * @event selectionchange
24114 * Fires when the selection changes
24115 * @param {SelectionModel} this
24117 "selectionchange" : true,
24119 * @event afterselectionchange
24120 * Fires after the selection changes (eg. by key press or clicking)
24121 * @param {SelectionModel} this
24123 "afterselectionchange" : true,
24125 * @event beforerowselect
24126 * Fires when a row is selected being selected, return false to cancel.
24127 * @param {SelectionModel} this
24128 * @param {Number} rowIndex The selected index
24129 * @param {Boolean} keepExisting False if other selections will be cleared
24131 "beforerowselect" : true,
24134 * Fires when a row is selected.
24135 * @param {SelectionModel} this
24136 * @param {Number} rowIndex The selected index
24137 * @param {Roo.data.Record} r The record
24139 "rowselect" : true,
24141 * @event rowdeselect
24142 * Fires when a row is deselected.
24143 * @param {SelectionModel} this
24144 * @param {Number} rowIndex The selected index
24146 "rowdeselect" : true
24148 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24149 this.locked = false;
24152 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24154 * @cfg {Boolean} singleSelect
24155 * True to allow selection of only one row at a time (defaults to false)
24157 singleSelect : false,
24160 initEvents : function()
24163 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24164 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24165 //}else{ // allow click to work like normal
24166 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24168 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24169 this.grid.on("rowclick", this.handleMouseDown, this);
24171 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24172 "up" : function(e){
24174 this.selectPrevious(e.shiftKey);
24175 }else if(this.last !== false && this.lastActive !== false){
24176 var last = this.last;
24177 this.selectRange(this.last, this.lastActive-1);
24178 this.grid.getView().focusRow(this.lastActive);
24179 if(last !== false){
24183 this.selectFirstRow();
24185 this.fireEvent("afterselectionchange", this);
24187 "down" : function(e){
24189 this.selectNext(e.shiftKey);
24190 }else if(this.last !== false && this.lastActive !== false){
24191 var last = this.last;
24192 this.selectRange(this.last, this.lastActive+1);
24193 this.grid.getView().focusRow(this.lastActive);
24194 if(last !== false){
24198 this.selectFirstRow();
24200 this.fireEvent("afterselectionchange", this);
24204 this.grid.store.on('load', function(){
24205 this.selections.clear();
24208 var view = this.grid.view;
24209 view.on("refresh", this.onRefresh, this);
24210 view.on("rowupdated", this.onRowUpdated, this);
24211 view.on("rowremoved", this.onRemove, this);
24216 onRefresh : function()
24218 var ds = this.grid.store, i, v = this.grid.view;
24219 var s = this.selections;
24220 s.each(function(r){
24221 if((i = ds.indexOfId(r.id)) != -1){
24230 onRemove : function(v, index, r){
24231 this.selections.remove(r);
24235 onRowUpdated : function(v, index, r){
24236 if(this.isSelected(r)){
24237 v.onRowSelect(index);
24243 * @param {Array} records The records to select
24244 * @param {Boolean} keepExisting (optional) True to keep existing selections
24246 selectRecords : function(records, keepExisting)
24249 this.clearSelections();
24251 var ds = this.grid.store;
24252 for(var i = 0, len = records.length; i < len; i++){
24253 this.selectRow(ds.indexOf(records[i]), true);
24258 * Gets the number of selected rows.
24261 getCount : function(){
24262 return this.selections.length;
24266 * Selects the first row in the grid.
24268 selectFirstRow : function(){
24273 * Select the last row.
24274 * @param {Boolean} keepExisting (optional) True to keep existing selections
24276 selectLastRow : function(keepExisting){
24277 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24278 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24282 * Selects the row immediately following the last selected row.
24283 * @param {Boolean} keepExisting (optional) True to keep existing selections
24285 selectNext : function(keepExisting)
24287 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24288 this.selectRow(this.last+1, keepExisting);
24289 this.grid.getView().focusRow(this.last);
24294 * Selects the row that precedes the last selected row.
24295 * @param {Boolean} keepExisting (optional) True to keep existing selections
24297 selectPrevious : function(keepExisting){
24299 this.selectRow(this.last-1, keepExisting);
24300 this.grid.getView().focusRow(this.last);
24305 * Returns the selected records
24306 * @return {Array} Array of selected records
24308 getSelections : function(){
24309 return [].concat(this.selections.items);
24313 * Returns the first selected record.
24316 getSelected : function(){
24317 return this.selections.itemAt(0);
24322 * Clears all selections.
24324 clearSelections : function(fast)
24330 var ds = this.grid.store;
24331 var s = this.selections;
24332 s.each(function(r){
24333 this.deselectRow(ds.indexOfId(r.id));
24337 this.selections.clear();
24344 * Selects all rows.
24346 selectAll : function(){
24350 this.selections.clear();
24351 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24352 this.selectRow(i, true);
24357 * Returns True if there is a selection.
24358 * @return {Boolean}
24360 hasSelection : function(){
24361 return this.selections.length > 0;
24365 * Returns True if the specified row is selected.
24366 * @param {Number/Record} record The record or index of the record to check
24367 * @return {Boolean}
24369 isSelected : function(index){
24370 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24371 return (r && this.selections.key(r.id) ? true : false);
24375 * Returns True if the specified record id is selected.
24376 * @param {String} id The id of record to check
24377 * @return {Boolean}
24379 isIdSelected : function(id){
24380 return (this.selections.key(id) ? true : false);
24385 handleMouseDBClick : function(e, t){
24389 handleMouseDown : function(e, t)
24391 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24392 if(this.isLocked() || rowIndex < 0 ){
24395 if(e.shiftKey && this.last !== false){
24396 var last = this.last;
24397 this.selectRange(last, rowIndex, e.ctrlKey);
24398 this.last = last; // reset the last
24402 var isSelected = this.isSelected(rowIndex);
24403 //Roo.log("select row:" + rowIndex);
24405 this.deselectRow(rowIndex);
24407 this.selectRow(rowIndex, true);
24411 if(e.button !== 0 && isSelected){
24412 alert('rowIndex 2: ' + rowIndex);
24413 view.focusRow(rowIndex);
24414 }else if(e.ctrlKey && isSelected){
24415 this.deselectRow(rowIndex);
24416 }else if(!isSelected){
24417 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24418 view.focusRow(rowIndex);
24422 this.fireEvent("afterselectionchange", this);
24425 handleDragableRowClick : function(grid, rowIndex, e)
24427 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24428 this.selectRow(rowIndex, false);
24429 grid.view.focusRow(rowIndex);
24430 this.fireEvent("afterselectionchange", this);
24435 * Selects multiple rows.
24436 * @param {Array} rows Array of the indexes of the row to select
24437 * @param {Boolean} keepExisting (optional) True to keep existing selections
24439 selectRows : function(rows, keepExisting){
24441 this.clearSelections();
24443 for(var i = 0, len = rows.length; i < len; i++){
24444 this.selectRow(rows[i], true);
24449 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24450 * @param {Number} startRow The index of the first row in the range
24451 * @param {Number} endRow The index of the last row in the range
24452 * @param {Boolean} keepExisting (optional) True to retain existing selections
24454 selectRange : function(startRow, endRow, keepExisting){
24459 this.clearSelections();
24461 if(startRow <= endRow){
24462 for(var i = startRow; i <= endRow; i++){
24463 this.selectRow(i, true);
24466 for(var i = startRow; i >= endRow; i--){
24467 this.selectRow(i, true);
24473 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24474 * @param {Number} startRow The index of the first row in the range
24475 * @param {Number} endRow The index of the last row in the range
24477 deselectRange : function(startRow, endRow, preventViewNotify){
24481 for(var i = startRow; i <= endRow; i++){
24482 this.deselectRow(i, preventViewNotify);
24488 * @param {Number} row The index of the row to select
24489 * @param {Boolean} keepExisting (optional) True to keep existing selections
24491 selectRow : function(index, keepExisting, preventViewNotify)
24493 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24496 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24497 if(!keepExisting || this.singleSelect){
24498 this.clearSelections();
24501 var r = this.grid.store.getAt(index);
24502 //console.log('selectRow - record id :' + r.id);
24504 this.selections.add(r);
24505 this.last = this.lastActive = index;
24506 if(!preventViewNotify){
24507 var proxy = new Roo.Element(
24508 this.grid.getRowDom(index)
24510 proxy.addClass('bg-info info');
24512 this.fireEvent("rowselect", this, index, r);
24513 this.fireEvent("selectionchange", this);
24519 * @param {Number} row The index of the row to deselect
24521 deselectRow : function(index, preventViewNotify)
24526 if(this.last == index){
24529 if(this.lastActive == index){
24530 this.lastActive = false;
24533 var r = this.grid.store.getAt(index);
24538 this.selections.remove(r);
24539 //.console.log('deselectRow - record id :' + r.id);
24540 if(!preventViewNotify){
24542 var proxy = new Roo.Element(
24543 this.grid.getRowDom(index)
24545 proxy.removeClass('bg-info info');
24547 this.fireEvent("rowdeselect", this, index);
24548 this.fireEvent("selectionchange", this);
24552 restoreLast : function(){
24554 this.last = this._last;
24559 acceptsNav : function(row, col, cm){
24560 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24564 onEditorKey : function(field, e){
24565 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24570 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24572 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24574 }else if(k == e.ENTER && !e.ctrlKey){
24578 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24580 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24582 }else if(k == e.ESC){
24586 g.startEditing(newCell[0], newCell[1]);
24592 * Ext JS Library 1.1.1
24593 * Copyright(c) 2006-2007, Ext JS, LLC.
24595 * Originally Released Under LGPL - original licence link has changed is not relivant.
24598 * <script type="text/javascript">
24602 * @class Roo.bootstrap.PagingToolbar
24603 * @extends Roo.bootstrap.NavSimplebar
24604 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24606 * Create a new PagingToolbar
24607 * @param {Object} config The config object
24608 * @param {Roo.data.Store} store
24610 Roo.bootstrap.PagingToolbar = function(config)
24612 // old args format still supported... - xtype is prefered..
24613 // created from xtype...
24615 this.ds = config.dataSource;
24617 if (config.store && !this.ds) {
24618 this.store= Roo.factory(config.store, Roo.data);
24619 this.ds = this.store;
24620 this.ds.xmodule = this.xmodule || false;
24623 this.toolbarItems = [];
24624 if (config.items) {
24625 this.toolbarItems = config.items;
24628 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24633 this.bind(this.ds);
24636 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24640 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24642 * @cfg {Roo.data.Store} dataSource
24643 * The underlying data store providing the paged data
24646 * @cfg {String/HTMLElement/Element} container
24647 * container The id or element that will contain the toolbar
24650 * @cfg {Boolean} displayInfo
24651 * True to display the displayMsg (defaults to false)
24654 * @cfg {Number} pageSize
24655 * The number of records to display per page (defaults to 20)
24659 * @cfg {String} displayMsg
24660 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24662 displayMsg : 'Displaying {0} - {1} of {2}',
24664 * @cfg {String} emptyMsg
24665 * The message to display when no records are found (defaults to "No data to display")
24667 emptyMsg : 'No data to display',
24669 * Customizable piece of the default paging text (defaults to "Page")
24672 beforePageText : "Page",
24674 * Customizable piece of the default paging text (defaults to "of %0")
24677 afterPageText : "of {0}",
24679 * Customizable piece of the default paging text (defaults to "First Page")
24682 firstText : "First Page",
24684 * Customizable piece of the default paging text (defaults to "Previous Page")
24687 prevText : "Previous Page",
24689 * Customizable piece of the default paging text (defaults to "Next Page")
24692 nextText : "Next Page",
24694 * Customizable piece of the default paging text (defaults to "Last Page")
24697 lastText : "Last Page",
24699 * Customizable piece of the default paging text (defaults to "Refresh")
24702 refreshText : "Refresh",
24706 onRender : function(ct, position)
24708 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24709 this.navgroup.parentId = this.id;
24710 this.navgroup.onRender(this.el, null);
24711 // add the buttons to the navgroup
24713 if(this.displayInfo){
24714 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24715 this.displayEl = this.el.select('.x-paging-info', true).first();
24716 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24717 // this.displayEl = navel.el.select('span',true).first();
24723 Roo.each(_this.buttons, function(e){ // this might need to use render????
24724 Roo.factory(e).render(_this.el);
24728 Roo.each(_this.toolbarItems, function(e) {
24729 _this.navgroup.addItem(e);
24733 this.first = this.navgroup.addItem({
24734 tooltip: this.firstText,
24736 icon : 'fa fa-backward',
24738 preventDefault: true,
24739 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24742 this.prev = this.navgroup.addItem({
24743 tooltip: this.prevText,
24745 icon : 'fa fa-step-backward',
24747 preventDefault: true,
24748 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24750 //this.addSeparator();
24753 var field = this.navgroup.addItem( {
24755 cls : 'x-paging-position',
24757 html : this.beforePageText +
24758 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24759 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24762 this.field = field.el.select('input', true).first();
24763 this.field.on("keydown", this.onPagingKeydown, this);
24764 this.field.on("focus", function(){this.dom.select();});
24767 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24768 //this.field.setHeight(18);
24769 //this.addSeparator();
24770 this.next = this.navgroup.addItem({
24771 tooltip: this.nextText,
24773 html : ' <i class="fa fa-step-forward">',
24775 preventDefault: true,
24776 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24778 this.last = this.navgroup.addItem({
24779 tooltip: this.lastText,
24780 icon : 'fa fa-forward',
24783 preventDefault: true,
24784 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24786 //this.addSeparator();
24787 this.loading = this.navgroup.addItem({
24788 tooltip: this.refreshText,
24789 icon: 'fa fa-refresh',
24790 preventDefault: true,
24791 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24797 updateInfo : function(){
24798 if(this.displayEl){
24799 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24800 var msg = count == 0 ?
24804 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24806 this.displayEl.update(msg);
24811 onLoad : function(ds, r, o)
24813 this.cursor = o.params.start ? o.params.start : 0;
24815 var d = this.getPageData(),
24820 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24821 this.field.dom.value = ap;
24822 this.first.setDisabled(ap == 1);
24823 this.prev.setDisabled(ap == 1);
24824 this.next.setDisabled(ap == ps);
24825 this.last.setDisabled(ap == ps);
24826 this.loading.enable();
24831 getPageData : function(){
24832 var total = this.ds.getTotalCount();
24835 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24836 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24841 onLoadError : function(){
24842 this.loading.enable();
24846 onPagingKeydown : function(e){
24847 var k = e.getKey();
24848 var d = this.getPageData();
24850 var v = this.field.dom.value, pageNum;
24851 if(!v || isNaN(pageNum = parseInt(v, 10))){
24852 this.field.dom.value = d.activePage;
24855 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24856 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24859 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))
24861 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24862 this.field.dom.value = pageNum;
24863 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24866 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24868 var v = this.field.dom.value, pageNum;
24869 var increment = (e.shiftKey) ? 10 : 1;
24870 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24873 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24874 this.field.dom.value = d.activePage;
24877 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24879 this.field.dom.value = parseInt(v, 10) + increment;
24880 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24881 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24888 beforeLoad : function(){
24890 this.loading.disable();
24895 onClick : function(which){
24904 ds.load({params:{start: 0, limit: this.pageSize}});
24907 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24910 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24913 var total = ds.getTotalCount();
24914 var extra = total % this.pageSize;
24915 var lastStart = extra ? (total - extra) : total-this.pageSize;
24916 ds.load({params:{start: lastStart, limit: this.pageSize}});
24919 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24925 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24926 * @param {Roo.data.Store} store The data store to unbind
24928 unbind : function(ds){
24929 ds.un("beforeload", this.beforeLoad, this);
24930 ds.un("load", this.onLoad, this);
24931 ds.un("loadexception", this.onLoadError, this);
24932 ds.un("remove", this.updateInfo, this);
24933 ds.un("add", this.updateInfo, this);
24934 this.ds = undefined;
24938 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24939 * @param {Roo.data.Store} store The data store to bind
24941 bind : function(ds){
24942 ds.on("beforeload", this.beforeLoad, this);
24943 ds.on("load", this.onLoad, this);
24944 ds.on("loadexception", this.onLoadError, this);
24945 ds.on("remove", this.updateInfo, this);
24946 ds.on("add", this.updateInfo, this);
24957 * @class Roo.bootstrap.MessageBar
24958 * @extends Roo.bootstrap.Component
24959 * Bootstrap MessageBar class
24960 * @cfg {String} html contents of the MessageBar
24961 * @cfg {String} weight (info | success | warning | danger) default info
24962 * @cfg {String} beforeClass insert the bar before the given class
24963 * @cfg {Boolean} closable (true | false) default false
24964 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24967 * Create a new Element
24968 * @param {Object} config The config object
24971 Roo.bootstrap.MessageBar = function(config){
24972 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24975 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24981 beforeClass: 'bootstrap-sticky-wrap',
24983 getAutoCreate : function(){
24987 cls: 'alert alert-dismissable alert-' + this.weight,
24992 html: this.html || ''
24998 cfg.cls += ' alert-messages-fixed';
25012 onRender : function(ct, position)
25014 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25017 var cfg = Roo.apply({}, this.getAutoCreate());
25021 cfg.cls += ' ' + this.cls;
25024 cfg.style = this.style;
25026 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25028 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25031 this.el.select('>button.close').on('click', this.hide, this);
25037 if (!this.rendered) {
25043 this.fireEvent('show', this);
25049 if (!this.rendered) {
25055 this.fireEvent('hide', this);
25058 update : function()
25060 // var e = this.el.dom.firstChild;
25062 // if(this.closable){
25063 // e = e.nextSibling;
25066 // e.data = this.html || '';
25068 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25084 * @class Roo.bootstrap.Graph
25085 * @extends Roo.bootstrap.Component
25086 * Bootstrap Graph class
25090 @cfg {String} graphtype bar | vbar | pie
25091 @cfg {number} g_x coodinator | centre x (pie)
25092 @cfg {number} g_y coodinator | centre y (pie)
25093 @cfg {number} g_r radius (pie)
25094 @cfg {number} g_height height of the chart (respected by all elements in the set)
25095 @cfg {number} g_width width of the chart (respected by all elements in the set)
25096 @cfg {Object} title The title of the chart
25099 -opts (object) options for the chart
25101 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25102 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25104 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.
25105 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25107 o stretch (boolean)
25109 -opts (object) options for the pie
25112 o startAngle (number)
25113 o endAngle (number)
25117 * Create a new Input
25118 * @param {Object} config The config object
25121 Roo.bootstrap.Graph = function(config){
25122 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25128 * The img click event for the img.
25129 * @param {Roo.EventObject} e
25135 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25146 //g_colors: this.colors,
25153 getAutoCreate : function(){
25164 onRender : function(ct,position){
25167 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25169 if (typeof(Raphael) == 'undefined') {
25170 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25174 this.raphael = Raphael(this.el.dom);
25176 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25177 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25178 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25179 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25181 r.text(160, 10, "Single Series Chart").attr(txtattr);
25182 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25183 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25184 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25186 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25187 r.barchart(330, 10, 300, 220, data1);
25188 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25189 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25192 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25193 // r.barchart(30, 30, 560, 250, xdata, {
25194 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25195 // axis : "0 0 1 1",
25196 // axisxlabels : xdata
25197 // //yvalues : cols,
25200 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25202 // this.load(null,xdata,{
25203 // axis : "0 0 1 1",
25204 // axisxlabels : xdata
25209 load : function(graphtype,xdata,opts)
25211 this.raphael.clear();
25213 graphtype = this.graphtype;
25218 var r = this.raphael,
25219 fin = function () {
25220 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25222 fout = function () {
25223 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25225 pfin = function() {
25226 this.sector.stop();
25227 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25230 this.label[0].stop();
25231 this.label[0].attr({ r: 7.5 });
25232 this.label[1].attr({ "font-weight": 800 });
25235 pfout = function() {
25236 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25239 this.label[0].animate({ r: 5 }, 500, "bounce");
25240 this.label[1].attr({ "font-weight": 400 });
25246 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25249 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25252 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25253 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25255 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25262 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25267 setTitle: function(o)
25272 initEvents: function() {
25275 this.el.on('click', this.onClick, this);
25279 onClick : function(e)
25281 Roo.log('img onclick');
25282 this.fireEvent('click', this, e);
25294 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25297 * @class Roo.bootstrap.dash.NumberBox
25298 * @extends Roo.bootstrap.Component
25299 * Bootstrap NumberBox class
25300 * @cfg {String} headline Box headline
25301 * @cfg {String} content Box content
25302 * @cfg {String} icon Box icon
25303 * @cfg {String} footer Footer text
25304 * @cfg {String} fhref Footer href
25307 * Create a new NumberBox
25308 * @param {Object} config The config object
25312 Roo.bootstrap.dash.NumberBox = function(config){
25313 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25317 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25326 getAutoCreate : function(){
25330 cls : 'small-box ',
25338 cls : 'roo-headline',
25339 html : this.headline
25343 cls : 'roo-content',
25344 html : this.content
25358 cls : 'ion ' + this.icon
25367 cls : 'small-box-footer',
25368 href : this.fhref || '#',
25372 cfg.cn.push(footer);
25379 onRender : function(ct,position){
25380 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25387 setHeadline: function (value)
25389 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25392 setFooter: function (value, href)
25394 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25397 this.el.select('a.small-box-footer',true).first().attr('href', href);
25402 setContent: function (value)
25404 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25407 initEvents: function()
25421 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25424 * @class Roo.bootstrap.dash.TabBox
25425 * @extends Roo.bootstrap.Component
25426 * Bootstrap TabBox class
25427 * @cfg {String} title Title of the TabBox
25428 * @cfg {String} icon Icon of the TabBox
25429 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25430 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25433 * Create a new TabBox
25434 * @param {Object} config The config object
25438 Roo.bootstrap.dash.TabBox = function(config){
25439 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25444 * When a pane is added
25445 * @param {Roo.bootstrap.dash.TabPane} pane
25449 * @event activatepane
25450 * When a pane is activated
25451 * @param {Roo.bootstrap.dash.TabPane} pane
25453 "activatepane" : true
25461 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25466 tabScrollable : false,
25468 getChildContainer : function()
25470 return this.el.select('.tab-content', true).first();
25473 getAutoCreate : function(){
25477 cls: 'pull-left header',
25485 cls: 'fa ' + this.icon
25491 cls: 'nav nav-tabs pull-right',
25497 if(this.tabScrollable){
25504 cls: 'nav nav-tabs pull-right',
25515 cls: 'nav-tabs-custom',
25520 cls: 'tab-content no-padding',
25528 initEvents : function()
25530 //Roo.log('add add pane handler');
25531 this.on('addpane', this.onAddPane, this);
25534 * Updates the box title
25535 * @param {String} html to set the title to.
25537 setTitle : function(value)
25539 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25541 onAddPane : function(pane)
25543 this.panes.push(pane);
25544 //Roo.log('addpane');
25546 // tabs are rendere left to right..
25547 if(!this.showtabs){
25551 var ctr = this.el.select('.nav-tabs', true).first();
25554 var existing = ctr.select('.nav-tab',true);
25555 var qty = existing.getCount();;
25558 var tab = ctr.createChild({
25560 cls : 'nav-tab' + (qty ? '' : ' active'),
25568 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25571 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25573 pane.el.addClass('active');
25578 onTabClick : function(ev,un,ob,pane)
25580 //Roo.log('tab - prev default');
25581 ev.preventDefault();
25584 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25585 pane.tab.addClass('active');
25586 //Roo.log(pane.title);
25587 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25588 // technically we should have a deactivate event.. but maybe add later.
25589 // and it should not de-activate the selected tab...
25590 this.fireEvent('activatepane', pane);
25591 pane.el.addClass('active');
25592 pane.fireEvent('activate');
25597 getActivePane : function()
25600 Roo.each(this.panes, function(p) {
25601 if(p.el.hasClass('active')){
25622 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25624 * @class Roo.bootstrap.TabPane
25625 * @extends Roo.bootstrap.Component
25626 * Bootstrap TabPane class
25627 * @cfg {Boolean} active (false | true) Default false
25628 * @cfg {String} title title of panel
25632 * Create a new TabPane
25633 * @param {Object} config The config object
25636 Roo.bootstrap.dash.TabPane = function(config){
25637 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25643 * When a pane is activated
25644 * @param {Roo.bootstrap.dash.TabPane} pane
25651 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25656 // the tabBox that this is attached to.
25659 getAutoCreate : function()
25667 cfg.cls += ' active';
25672 initEvents : function()
25674 //Roo.log('trigger add pane handler');
25675 this.parent().fireEvent('addpane', this)
25679 * Updates the tab title
25680 * @param {String} html to set the title to.
25682 setTitle: function(str)
25688 this.tab.select('a', true).first().dom.innerHTML = str;
25705 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25708 * @class Roo.bootstrap.menu.Menu
25709 * @extends Roo.bootstrap.Component
25710 * Bootstrap Menu class - container for Menu
25711 * @cfg {String} html Text of the menu
25712 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25713 * @cfg {String} icon Font awesome icon
25714 * @cfg {String} pos Menu align to (top | bottom) default bottom
25718 * Create a new Menu
25719 * @param {Object} config The config object
25723 Roo.bootstrap.menu.Menu = function(config){
25724 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25728 * @event beforeshow
25729 * Fires before this menu is displayed
25730 * @param {Roo.bootstrap.menu.Menu} this
25734 * @event beforehide
25735 * Fires before this menu is hidden
25736 * @param {Roo.bootstrap.menu.Menu} this
25741 * Fires after this menu is displayed
25742 * @param {Roo.bootstrap.menu.Menu} this
25747 * Fires after this menu is hidden
25748 * @param {Roo.bootstrap.menu.Menu} this
25753 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25754 * @param {Roo.bootstrap.menu.Menu} this
25755 * @param {Roo.EventObject} e
25762 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25766 weight : 'default',
25771 getChildContainer : function() {
25772 if(this.isSubMenu){
25776 return this.el.select('ul.dropdown-menu', true).first();
25779 getAutoCreate : function()
25784 cls : 'roo-menu-text',
25792 cls : 'fa ' + this.icon
25803 cls : 'dropdown-button btn btn-' + this.weight,
25808 cls : 'dropdown-toggle btn btn-' + this.weight,
25818 cls : 'dropdown-menu'
25824 if(this.pos == 'top'){
25825 cfg.cls += ' dropup';
25828 if(this.isSubMenu){
25831 cls : 'dropdown-menu'
25838 onRender : function(ct, position)
25840 this.isSubMenu = ct.hasClass('dropdown-submenu');
25842 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25845 initEvents : function()
25847 if(this.isSubMenu){
25851 this.hidden = true;
25853 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25854 this.triggerEl.on('click', this.onTriggerPress, this);
25856 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25857 this.buttonEl.on('click', this.onClick, this);
25863 if(this.isSubMenu){
25867 return this.el.select('ul.dropdown-menu', true).first();
25870 onClick : function(e)
25872 this.fireEvent("click", this, e);
25875 onTriggerPress : function(e)
25877 if (this.isVisible()) {
25884 isVisible : function(){
25885 return !this.hidden;
25890 this.fireEvent("beforeshow", this);
25892 this.hidden = false;
25893 this.el.addClass('open');
25895 Roo.get(document).on("mouseup", this.onMouseUp, this);
25897 this.fireEvent("show", this);
25904 this.fireEvent("beforehide", this);
25906 this.hidden = true;
25907 this.el.removeClass('open');
25909 Roo.get(document).un("mouseup", this.onMouseUp);
25911 this.fireEvent("hide", this);
25914 onMouseUp : function()
25928 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25931 * @class Roo.bootstrap.menu.Item
25932 * @extends Roo.bootstrap.Component
25933 * Bootstrap MenuItem class
25934 * @cfg {Boolean} submenu (true | false) default false
25935 * @cfg {String} html text of the item
25936 * @cfg {String} href the link
25937 * @cfg {Boolean} disable (true | false) default false
25938 * @cfg {Boolean} preventDefault (true | false) default true
25939 * @cfg {String} icon Font awesome icon
25940 * @cfg {String} pos Submenu align to (left | right) default right
25944 * Create a new Item
25945 * @param {Object} config The config object
25949 Roo.bootstrap.menu.Item = function(config){
25950 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25954 * Fires when the mouse is hovering over this menu
25955 * @param {Roo.bootstrap.menu.Item} this
25956 * @param {Roo.EventObject} e
25961 * Fires when the mouse exits this menu
25962 * @param {Roo.bootstrap.menu.Item} this
25963 * @param {Roo.EventObject} e
25969 * The raw click event for the entire grid.
25970 * @param {Roo.EventObject} e
25976 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25981 preventDefault: true,
25986 getAutoCreate : function()
25991 cls : 'roo-menu-item-text',
25999 cls : 'fa ' + this.icon
26008 href : this.href || '#',
26015 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26019 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26021 if(this.pos == 'left'){
26022 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26029 initEvents : function()
26031 this.el.on('mouseover', this.onMouseOver, this);
26032 this.el.on('mouseout', this.onMouseOut, this);
26034 this.el.select('a', true).first().on('click', this.onClick, this);
26038 onClick : function(e)
26040 if(this.preventDefault){
26041 e.preventDefault();
26044 this.fireEvent("click", this, e);
26047 onMouseOver : function(e)
26049 if(this.submenu && this.pos == 'left'){
26050 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26053 this.fireEvent("mouseover", this, e);
26056 onMouseOut : function(e)
26058 this.fireEvent("mouseout", this, e);
26070 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26073 * @class Roo.bootstrap.menu.Separator
26074 * @extends Roo.bootstrap.Component
26075 * Bootstrap Separator class
26078 * Create a new Separator
26079 * @param {Object} config The config object
26083 Roo.bootstrap.menu.Separator = function(config){
26084 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26087 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26089 getAutoCreate : function(){
26110 * @class Roo.bootstrap.Tooltip
26111 * Bootstrap Tooltip class
26112 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26113 * to determine which dom element triggers the tooltip.
26115 * It needs to add support for additional attributes like tooltip-position
26118 * Create a new Toolti
26119 * @param {Object} config The config object
26122 Roo.bootstrap.Tooltip = function(config){
26123 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26125 this.alignment = Roo.bootstrap.Tooltip.alignment;
26127 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26128 this.alignment = config.alignment;
26133 Roo.apply(Roo.bootstrap.Tooltip, {
26135 * @function init initialize tooltip monitoring.
26139 currentTip : false,
26140 currentRegion : false,
26146 Roo.get(document).on('mouseover', this.enter ,this);
26147 Roo.get(document).on('mouseout', this.leave, this);
26150 this.currentTip = new Roo.bootstrap.Tooltip();
26153 enter : function(ev)
26155 var dom = ev.getTarget();
26157 //Roo.log(['enter',dom]);
26158 var el = Roo.fly(dom);
26159 if (this.currentEl) {
26161 //Roo.log(this.currentEl);
26162 //Roo.log(this.currentEl.contains(dom));
26163 if (this.currentEl == el) {
26166 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26172 if (this.currentTip.el) {
26173 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26177 if(!el || el.dom == document){
26183 // you can not look for children, as if el is the body.. then everythign is the child..
26184 if (!el.attr('tooltip')) { //
26185 if (!el.select("[tooltip]").elements.length) {
26188 // is the mouse over this child...?
26189 bindEl = el.select("[tooltip]").first();
26190 var xy = ev.getXY();
26191 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26192 //Roo.log("not in region.");
26195 //Roo.log("child element over..");
26198 this.currentEl = bindEl;
26199 this.currentTip.bind(bindEl);
26200 this.currentRegion = Roo.lib.Region.getRegion(dom);
26201 this.currentTip.enter();
26204 leave : function(ev)
26206 var dom = ev.getTarget();
26207 //Roo.log(['leave',dom]);
26208 if (!this.currentEl) {
26213 if (dom != this.currentEl.dom) {
26216 var xy = ev.getXY();
26217 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26220 // only activate leave if mouse cursor is outside... bounding box..
26225 if (this.currentTip) {
26226 this.currentTip.leave();
26228 //Roo.log('clear currentEl');
26229 this.currentEl = false;
26234 'left' : ['r-l', [-2,0], 'right'],
26235 'right' : ['l-r', [2,0], 'left'],
26236 'bottom' : ['t-b', [0,2], 'top'],
26237 'top' : [ 'b-t', [0,-2], 'bottom']
26243 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26248 delay : null, // can be { show : 300 , hide: 500}
26252 hoverState : null, //???
26254 placement : 'bottom',
26258 getAutoCreate : function(){
26265 cls : 'tooltip-arrow'
26268 cls : 'tooltip-inner'
26275 bind : function(el)
26281 enter : function () {
26283 if (this.timeout != null) {
26284 clearTimeout(this.timeout);
26287 this.hoverState = 'in';
26288 //Roo.log("enter - show");
26289 if (!this.delay || !this.delay.show) {
26294 this.timeout = setTimeout(function () {
26295 if (_t.hoverState == 'in') {
26298 }, this.delay.show);
26302 clearTimeout(this.timeout);
26304 this.hoverState = 'out';
26305 if (!this.delay || !this.delay.hide) {
26311 this.timeout = setTimeout(function () {
26312 //Roo.log("leave - timeout");
26314 if (_t.hoverState == 'out') {
26316 Roo.bootstrap.Tooltip.currentEl = false;
26321 show : function (msg)
26324 this.render(document.body);
26327 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26329 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26331 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26333 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26335 var placement = typeof this.placement == 'function' ?
26336 this.placement.call(this, this.el, on_el) :
26339 var autoToken = /\s?auto?\s?/i;
26340 var autoPlace = autoToken.test(placement);
26342 placement = placement.replace(autoToken, '') || 'top';
26346 //this.el.setXY([0,0]);
26348 //this.el.dom.style.display='block';
26350 //this.el.appendTo(on_el);
26352 var p = this.getPosition();
26353 var box = this.el.getBox();
26359 var align = this.alignment[placement];
26361 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26363 if(placement == 'top' || placement == 'bottom'){
26365 placement = 'right';
26368 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26369 placement = 'left';
26372 var scroll = Roo.select('body', true).first().getScroll();
26374 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26378 align = this.alignment[placement];
26381 this.el.alignTo(this.bindEl, align[0],align[1]);
26382 //var arrow = this.el.select('.arrow',true).first();
26383 //arrow.set(align[2],
26385 this.el.addClass(placement);
26387 this.el.addClass('in fade');
26389 this.hoverState = null;
26391 if (this.el.hasClass('fade')) {
26402 //this.el.setXY([0,0]);
26403 this.el.removeClass('in');
26419 * @class Roo.bootstrap.LocationPicker
26420 * @extends Roo.bootstrap.Component
26421 * Bootstrap LocationPicker class
26422 * @cfg {Number} latitude Position when init default 0
26423 * @cfg {Number} longitude Position when init default 0
26424 * @cfg {Number} zoom default 15
26425 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26426 * @cfg {Boolean} mapTypeControl default false
26427 * @cfg {Boolean} disableDoubleClickZoom default false
26428 * @cfg {Boolean} scrollwheel default true
26429 * @cfg {Boolean} streetViewControl default false
26430 * @cfg {Number} radius default 0
26431 * @cfg {String} locationName
26432 * @cfg {Boolean} draggable default true
26433 * @cfg {Boolean} enableAutocomplete default false
26434 * @cfg {Boolean} enableReverseGeocode default true
26435 * @cfg {String} markerTitle
26438 * Create a new LocationPicker
26439 * @param {Object} config The config object
26443 Roo.bootstrap.LocationPicker = function(config){
26445 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26450 * Fires when the picker initialized.
26451 * @param {Roo.bootstrap.LocationPicker} this
26452 * @param {Google Location} location
26456 * @event positionchanged
26457 * Fires when the picker position changed.
26458 * @param {Roo.bootstrap.LocationPicker} this
26459 * @param {Google Location} location
26461 positionchanged : true,
26464 * Fires when the map resize.
26465 * @param {Roo.bootstrap.LocationPicker} this
26470 * Fires when the map show.
26471 * @param {Roo.bootstrap.LocationPicker} this
26476 * Fires when the map hide.
26477 * @param {Roo.bootstrap.LocationPicker} this
26482 * Fires when click the map.
26483 * @param {Roo.bootstrap.LocationPicker} this
26484 * @param {Map event} e
26488 * @event mapRightClick
26489 * Fires when right click the map.
26490 * @param {Roo.bootstrap.LocationPicker} this
26491 * @param {Map event} e
26493 mapRightClick : true,
26495 * @event markerClick
26496 * Fires when click the marker.
26497 * @param {Roo.bootstrap.LocationPicker} this
26498 * @param {Map event} e
26500 markerClick : true,
26502 * @event markerRightClick
26503 * Fires when right click the marker.
26504 * @param {Roo.bootstrap.LocationPicker} this
26505 * @param {Map event} e
26507 markerRightClick : true,
26509 * @event OverlayViewDraw
26510 * Fires when OverlayView Draw
26511 * @param {Roo.bootstrap.LocationPicker} this
26513 OverlayViewDraw : true,
26515 * @event OverlayViewOnAdd
26516 * Fires when OverlayView Draw
26517 * @param {Roo.bootstrap.LocationPicker} this
26519 OverlayViewOnAdd : true,
26521 * @event OverlayViewOnRemove
26522 * Fires when OverlayView Draw
26523 * @param {Roo.bootstrap.LocationPicker} this
26525 OverlayViewOnRemove : true,
26527 * @event OverlayViewShow
26528 * Fires when OverlayView Draw
26529 * @param {Roo.bootstrap.LocationPicker} this
26530 * @param {Pixel} cpx
26532 OverlayViewShow : true,
26534 * @event OverlayViewHide
26535 * Fires when OverlayView Draw
26536 * @param {Roo.bootstrap.LocationPicker} this
26538 OverlayViewHide : true,
26540 * @event loadexception
26541 * Fires when load google lib failed.
26542 * @param {Roo.bootstrap.LocationPicker} this
26544 loadexception : true
26549 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26551 gMapContext: false,
26557 mapTypeControl: false,
26558 disableDoubleClickZoom: false,
26560 streetViewControl: false,
26564 enableAutocomplete: false,
26565 enableReverseGeocode: true,
26568 getAutoCreate: function()
26573 cls: 'roo-location-picker'
26579 initEvents: function(ct, position)
26581 if(!this.el.getWidth() || this.isApplied()){
26585 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26590 initial: function()
26592 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26593 this.fireEvent('loadexception', this);
26597 if(!this.mapTypeId){
26598 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26601 this.gMapContext = this.GMapContext();
26603 this.initOverlayView();
26605 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26609 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26610 _this.setPosition(_this.gMapContext.marker.position);
26613 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26614 _this.fireEvent('mapClick', this, event);
26618 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26619 _this.fireEvent('mapRightClick', this, event);
26623 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26624 _this.fireEvent('markerClick', this, event);
26628 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26629 _this.fireEvent('markerRightClick', this, event);
26633 this.setPosition(this.gMapContext.location);
26635 this.fireEvent('initial', this, this.gMapContext.location);
26638 initOverlayView: function()
26642 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26646 _this.fireEvent('OverlayViewDraw', _this);
26651 _this.fireEvent('OverlayViewOnAdd', _this);
26654 onRemove: function()
26656 _this.fireEvent('OverlayViewOnRemove', _this);
26659 show: function(cpx)
26661 _this.fireEvent('OverlayViewShow', _this, cpx);
26666 _this.fireEvent('OverlayViewHide', _this);
26672 fromLatLngToContainerPixel: function(event)
26674 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26677 isApplied: function()
26679 return this.getGmapContext() == false ? false : true;
26682 getGmapContext: function()
26684 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26687 GMapContext: function()
26689 var position = new google.maps.LatLng(this.latitude, this.longitude);
26691 var _map = new google.maps.Map(this.el.dom, {
26694 mapTypeId: this.mapTypeId,
26695 mapTypeControl: this.mapTypeControl,
26696 disableDoubleClickZoom: this.disableDoubleClickZoom,
26697 scrollwheel: this.scrollwheel,
26698 streetViewControl: this.streetViewControl,
26699 locationName: this.locationName,
26700 draggable: this.draggable,
26701 enableAutocomplete: this.enableAutocomplete,
26702 enableReverseGeocode: this.enableReverseGeocode
26705 var _marker = new google.maps.Marker({
26706 position: position,
26708 title: this.markerTitle,
26709 draggable: this.draggable
26716 location: position,
26717 radius: this.radius,
26718 locationName: this.locationName,
26719 addressComponents: {
26720 formatted_address: null,
26721 addressLine1: null,
26722 addressLine2: null,
26724 streetNumber: null,
26728 stateOrProvince: null
26731 domContainer: this.el.dom,
26732 geodecoder: new google.maps.Geocoder()
26736 drawCircle: function(center, radius, options)
26738 if (this.gMapContext.circle != null) {
26739 this.gMapContext.circle.setMap(null);
26743 options = Roo.apply({}, options, {
26744 strokeColor: "#0000FF",
26745 strokeOpacity: .35,
26747 fillColor: "#0000FF",
26751 options.map = this.gMapContext.map;
26752 options.radius = radius;
26753 options.center = center;
26754 this.gMapContext.circle = new google.maps.Circle(options);
26755 return this.gMapContext.circle;
26761 setPosition: function(location)
26763 this.gMapContext.location = location;
26764 this.gMapContext.marker.setPosition(location);
26765 this.gMapContext.map.panTo(location);
26766 this.drawCircle(location, this.gMapContext.radius, {});
26770 if (this.gMapContext.settings.enableReverseGeocode) {
26771 this.gMapContext.geodecoder.geocode({
26772 latLng: this.gMapContext.location
26773 }, function(results, status) {
26775 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26776 _this.gMapContext.locationName = results[0].formatted_address;
26777 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26779 _this.fireEvent('positionchanged', this, location);
26786 this.fireEvent('positionchanged', this, location);
26791 google.maps.event.trigger(this.gMapContext.map, "resize");
26793 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26795 this.fireEvent('resize', this);
26798 setPositionByLatLng: function(latitude, longitude)
26800 this.setPosition(new google.maps.LatLng(latitude, longitude));
26803 getCurrentPosition: function()
26806 latitude: this.gMapContext.location.lat(),
26807 longitude: this.gMapContext.location.lng()
26811 getAddressName: function()
26813 return this.gMapContext.locationName;
26816 getAddressComponents: function()
26818 return this.gMapContext.addressComponents;
26821 address_component_from_google_geocode: function(address_components)
26825 for (var i = 0; i < address_components.length; i++) {
26826 var component = address_components[i];
26827 if (component.types.indexOf("postal_code") >= 0) {
26828 result.postalCode = component.short_name;
26829 } else if (component.types.indexOf("street_number") >= 0) {
26830 result.streetNumber = component.short_name;
26831 } else if (component.types.indexOf("route") >= 0) {
26832 result.streetName = component.short_name;
26833 } else if (component.types.indexOf("neighborhood") >= 0) {
26834 result.city = component.short_name;
26835 } else if (component.types.indexOf("locality") >= 0) {
26836 result.city = component.short_name;
26837 } else if (component.types.indexOf("sublocality") >= 0) {
26838 result.district = component.short_name;
26839 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26840 result.stateOrProvince = component.short_name;
26841 } else if (component.types.indexOf("country") >= 0) {
26842 result.country = component.short_name;
26846 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26847 result.addressLine2 = "";
26851 setZoomLevel: function(zoom)
26853 this.gMapContext.map.setZoom(zoom);
26866 this.fireEvent('show', this);
26877 this.fireEvent('hide', this);
26882 Roo.apply(Roo.bootstrap.LocationPicker, {
26884 OverlayView : function(map, options)
26886 options = options || {};
26900 * @class Roo.bootstrap.Alert
26901 * @extends Roo.bootstrap.Component
26902 * Bootstrap Alert class
26903 * @cfg {String} title The title of alert
26904 * @cfg {String} html The content of alert
26905 * @cfg {String} weight ( success | info | warning | danger )
26906 * @cfg {String} faicon font-awesomeicon
26909 * Create a new alert
26910 * @param {Object} config The config object
26914 Roo.bootstrap.Alert = function(config){
26915 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26919 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26926 getAutoCreate : function()
26935 cls : 'roo-alert-icon'
26940 cls : 'roo-alert-title',
26945 cls : 'roo-alert-text',
26952 cfg.cn[0].cls += ' fa ' + this.faicon;
26956 cfg.cls += ' alert-' + this.weight;
26962 initEvents: function()
26964 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26967 setTitle : function(str)
26969 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26972 setText : function(str)
26974 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26977 setWeight : function(weight)
26980 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26983 this.weight = weight;
26985 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26988 setIcon : function(icon)
26991 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26994 this.faicon = icon;
26996 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27017 * @class Roo.bootstrap.UploadCropbox
27018 * @extends Roo.bootstrap.Component
27019 * Bootstrap UploadCropbox class
27020 * @cfg {String} emptyText show when image has been loaded
27021 * @cfg {String} rotateNotify show when image too small to rotate
27022 * @cfg {Number} errorTimeout default 3000
27023 * @cfg {Number} minWidth default 300
27024 * @cfg {Number} minHeight default 300
27025 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27026 * @cfg {Boolean} isDocument (true|false) default false
27027 * @cfg {String} url action url
27028 * @cfg {String} paramName default 'imageUpload'
27029 * @cfg {String} method default POST
27030 * @cfg {Boolean} loadMask (true|false) default true
27031 * @cfg {Boolean} loadingText default 'Loading...'
27034 * Create a new UploadCropbox
27035 * @param {Object} config The config object
27038 Roo.bootstrap.UploadCropbox = function(config){
27039 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27043 * @event beforeselectfile
27044 * Fire before select file
27045 * @param {Roo.bootstrap.UploadCropbox} this
27047 "beforeselectfile" : true,
27050 * Fire after initEvent
27051 * @param {Roo.bootstrap.UploadCropbox} this
27056 * Fire after initEvent
27057 * @param {Roo.bootstrap.UploadCropbox} this
27058 * @param {String} data
27063 * Fire when preparing the file data
27064 * @param {Roo.bootstrap.UploadCropbox} this
27065 * @param {Object} file
27070 * Fire when get exception
27071 * @param {Roo.bootstrap.UploadCropbox} this
27072 * @param {XMLHttpRequest} xhr
27074 "exception" : true,
27076 * @event beforeloadcanvas
27077 * Fire before load the canvas
27078 * @param {Roo.bootstrap.UploadCropbox} this
27079 * @param {String} src
27081 "beforeloadcanvas" : true,
27084 * Fire when trash image
27085 * @param {Roo.bootstrap.UploadCropbox} this
27090 * Fire when download the image
27091 * @param {Roo.bootstrap.UploadCropbox} this
27095 * @event footerbuttonclick
27096 * Fire when footerbuttonclick
27097 * @param {Roo.bootstrap.UploadCropbox} this
27098 * @param {String} type
27100 "footerbuttonclick" : true,
27104 * @param {Roo.bootstrap.UploadCropbox} this
27109 * Fire when rotate the image
27110 * @param {Roo.bootstrap.UploadCropbox} this
27111 * @param {String} pos
27116 * Fire when inspect the file
27117 * @param {Roo.bootstrap.UploadCropbox} this
27118 * @param {Object} file
27123 * Fire when xhr upload the file
27124 * @param {Roo.bootstrap.UploadCropbox} this
27125 * @param {Object} data
27130 * Fire when arrange the file data
27131 * @param {Roo.bootstrap.UploadCropbox} this
27132 * @param {Object} formData
27137 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27140 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27142 emptyText : 'Click to upload image',
27143 rotateNotify : 'Image is too small to rotate',
27144 errorTimeout : 3000,
27158 cropType : 'image/jpeg',
27160 canvasLoaded : false,
27161 isDocument : false,
27163 paramName : 'imageUpload',
27165 loadingText : 'Loading...',
27168 getAutoCreate : function()
27172 cls : 'roo-upload-cropbox',
27176 cls : 'roo-upload-cropbox-selector',
27181 cls : 'roo-upload-cropbox-body',
27182 style : 'cursor:pointer',
27186 cls : 'roo-upload-cropbox-preview'
27190 cls : 'roo-upload-cropbox-thumb'
27194 cls : 'roo-upload-cropbox-empty-notify',
27195 html : this.emptyText
27199 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27200 html : this.rotateNotify
27206 cls : 'roo-upload-cropbox-footer',
27209 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27219 onRender : function(ct, position)
27221 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27223 if (this.buttons.length) {
27225 Roo.each(this.buttons, function(bb) {
27227 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27229 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27235 this.maskEl = this.el;
27239 initEvents : function()
27241 this.urlAPI = (window.createObjectURL && window) ||
27242 (window.URL && URL.revokeObjectURL && URL) ||
27243 (window.webkitURL && webkitURL);
27245 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27246 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27248 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27249 this.selectorEl.hide();
27251 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27252 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27254 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27255 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27256 this.thumbEl.hide();
27258 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27259 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27261 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27262 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27263 this.errorEl.hide();
27265 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27266 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27267 this.footerEl.hide();
27269 this.setThumbBoxSize();
27275 this.fireEvent('initial', this);
27282 window.addEventListener("resize", function() { _this.resize(); } );
27284 this.bodyEl.on('click', this.beforeSelectFile, this);
27287 this.bodyEl.on('touchstart', this.onTouchStart, this);
27288 this.bodyEl.on('touchmove', this.onTouchMove, this);
27289 this.bodyEl.on('touchend', this.onTouchEnd, this);
27293 this.bodyEl.on('mousedown', this.onMouseDown, this);
27294 this.bodyEl.on('mousemove', this.onMouseMove, this);
27295 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27296 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27297 Roo.get(document).on('mouseup', this.onMouseUp, this);
27300 this.selectorEl.on('change', this.onFileSelected, this);
27306 this.baseScale = 1;
27308 this.baseRotate = 1;
27309 this.dragable = false;
27310 this.pinching = false;
27313 this.cropData = false;
27314 this.notifyEl.dom.innerHTML = this.emptyText;
27316 this.selectorEl.dom.value = '';
27320 resize : function()
27322 if(this.fireEvent('resize', this) != false){
27323 this.setThumbBoxPosition();
27324 this.setCanvasPosition();
27328 onFooterButtonClick : function(e, el, o, type)
27331 case 'rotate-left' :
27332 this.onRotateLeft(e);
27334 case 'rotate-right' :
27335 this.onRotateRight(e);
27338 this.beforeSelectFile(e);
27353 this.fireEvent('footerbuttonclick', this, type);
27356 beforeSelectFile : function(e)
27358 e.preventDefault();
27360 if(this.fireEvent('beforeselectfile', this) != false){
27361 this.selectorEl.dom.click();
27365 onFileSelected : function(e)
27367 e.preventDefault();
27369 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27373 var file = this.selectorEl.dom.files[0];
27375 if(this.fireEvent('inspect', this, file) != false){
27376 this.prepare(file);
27381 trash : function(e)
27383 this.fireEvent('trash', this);
27386 download : function(e)
27388 this.fireEvent('download', this);
27391 loadCanvas : function(src)
27393 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27397 this.imageEl = document.createElement('img');
27401 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27403 this.imageEl.src = src;
27407 onLoadCanvas : function()
27409 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27410 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27412 this.bodyEl.un('click', this.beforeSelectFile, this);
27414 this.notifyEl.hide();
27415 this.thumbEl.show();
27416 this.footerEl.show();
27418 this.baseRotateLevel();
27420 if(this.isDocument){
27421 this.setThumbBoxSize();
27424 this.setThumbBoxPosition();
27426 this.baseScaleLevel();
27432 this.canvasLoaded = true;
27435 this.maskEl.unmask();
27440 setCanvasPosition : function()
27442 if(!this.canvasEl){
27446 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27447 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27449 this.previewEl.setLeft(pw);
27450 this.previewEl.setTop(ph);
27454 onMouseDown : function(e)
27458 this.dragable = true;
27459 this.pinching = false;
27461 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27462 this.dragable = false;
27466 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27467 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27471 onMouseMove : function(e)
27475 if(!this.canvasLoaded){
27479 if (!this.dragable){
27483 var minX = Math.ceil(this.thumbEl.getLeft(true));
27484 var minY = Math.ceil(this.thumbEl.getTop(true));
27486 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27487 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27489 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27490 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27492 x = x - this.mouseX;
27493 y = y - this.mouseY;
27495 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27496 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27498 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27499 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27501 this.previewEl.setLeft(bgX);
27502 this.previewEl.setTop(bgY);
27504 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27505 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27508 onMouseUp : function(e)
27512 this.dragable = false;
27515 onMouseWheel : function(e)
27519 this.startScale = this.scale;
27521 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27523 if(!this.zoomable()){
27524 this.scale = this.startScale;
27533 zoomable : function()
27535 var minScale = this.thumbEl.getWidth() / this.minWidth;
27537 if(this.minWidth < this.minHeight){
27538 minScale = this.thumbEl.getHeight() / this.minHeight;
27541 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27542 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27546 (this.rotate == 0 || this.rotate == 180) &&
27548 width > this.imageEl.OriginWidth ||
27549 height > this.imageEl.OriginHeight ||
27550 (width < this.minWidth && height < this.minHeight)
27558 (this.rotate == 90 || this.rotate == 270) &&
27560 width > this.imageEl.OriginWidth ||
27561 height > this.imageEl.OriginHeight ||
27562 (width < this.minHeight && height < this.minWidth)
27569 !this.isDocument &&
27570 (this.rotate == 0 || this.rotate == 180) &&
27572 width < this.minWidth ||
27573 width > this.imageEl.OriginWidth ||
27574 height < this.minHeight ||
27575 height > this.imageEl.OriginHeight
27582 !this.isDocument &&
27583 (this.rotate == 90 || this.rotate == 270) &&
27585 width < this.minHeight ||
27586 width > this.imageEl.OriginWidth ||
27587 height < this.minWidth ||
27588 height > this.imageEl.OriginHeight
27598 onRotateLeft : function(e)
27600 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27602 var minScale = this.thumbEl.getWidth() / this.minWidth;
27604 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27605 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27607 this.startScale = this.scale;
27609 while (this.getScaleLevel() < minScale){
27611 this.scale = this.scale + 1;
27613 if(!this.zoomable()){
27618 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27619 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27624 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27631 this.scale = this.startScale;
27633 this.onRotateFail();
27638 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27640 if(this.isDocument){
27641 this.setThumbBoxSize();
27642 this.setThumbBoxPosition();
27643 this.setCanvasPosition();
27648 this.fireEvent('rotate', this, 'left');
27652 onRotateRight : function(e)
27654 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27656 var minScale = this.thumbEl.getWidth() / this.minWidth;
27658 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27659 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27661 this.startScale = this.scale;
27663 while (this.getScaleLevel() < minScale){
27665 this.scale = this.scale + 1;
27667 if(!this.zoomable()){
27672 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27673 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27678 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27685 this.scale = this.startScale;
27687 this.onRotateFail();
27692 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27694 if(this.isDocument){
27695 this.setThumbBoxSize();
27696 this.setThumbBoxPosition();
27697 this.setCanvasPosition();
27702 this.fireEvent('rotate', this, 'right');
27705 onRotateFail : function()
27707 this.errorEl.show(true);
27711 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27716 this.previewEl.dom.innerHTML = '';
27718 var canvasEl = document.createElement("canvas");
27720 var contextEl = canvasEl.getContext("2d");
27722 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27723 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27724 var center = this.imageEl.OriginWidth / 2;
27726 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27727 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27728 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27729 center = this.imageEl.OriginHeight / 2;
27732 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27734 contextEl.translate(center, center);
27735 contextEl.rotate(this.rotate * Math.PI / 180);
27737 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27739 this.canvasEl = document.createElement("canvas");
27741 this.contextEl = this.canvasEl.getContext("2d");
27743 switch (this.rotate) {
27746 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27747 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27749 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27754 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27755 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27757 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27758 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);
27762 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27767 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27768 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27770 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27771 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);
27775 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);
27780 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27781 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27783 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27784 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27788 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);
27795 this.previewEl.appendChild(this.canvasEl);
27797 this.setCanvasPosition();
27802 if(!this.canvasLoaded){
27806 var imageCanvas = document.createElement("canvas");
27808 var imageContext = imageCanvas.getContext("2d");
27810 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27811 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27813 var center = imageCanvas.width / 2;
27815 imageContext.translate(center, center);
27817 imageContext.rotate(this.rotate * Math.PI / 180);
27819 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27821 var canvas = document.createElement("canvas");
27823 var context = canvas.getContext("2d");
27825 canvas.width = this.minWidth;
27826 canvas.height = this.minHeight;
27828 switch (this.rotate) {
27831 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27832 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27834 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27835 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27837 var targetWidth = this.minWidth - 2 * x;
27838 var targetHeight = this.minHeight - 2 * y;
27842 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27843 scale = targetWidth / width;
27846 if(x > 0 && y == 0){
27847 scale = targetHeight / height;
27850 if(x > 0 && y > 0){
27851 scale = targetWidth / width;
27853 if(width < height){
27854 scale = targetHeight / height;
27858 context.scale(scale, scale);
27860 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27861 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27863 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27864 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27866 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27871 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27872 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27874 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27875 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27877 var targetWidth = this.minWidth - 2 * x;
27878 var targetHeight = this.minHeight - 2 * y;
27882 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27883 scale = targetWidth / width;
27886 if(x > 0 && y == 0){
27887 scale = targetHeight / height;
27890 if(x > 0 && y > 0){
27891 scale = targetWidth / width;
27893 if(width < height){
27894 scale = targetHeight / height;
27898 context.scale(scale, scale);
27900 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27901 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27903 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27904 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27906 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27908 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27913 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27914 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27916 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27917 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27919 var targetWidth = this.minWidth - 2 * x;
27920 var targetHeight = this.minHeight - 2 * y;
27924 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27925 scale = targetWidth / width;
27928 if(x > 0 && y == 0){
27929 scale = targetHeight / height;
27932 if(x > 0 && y > 0){
27933 scale = targetWidth / width;
27935 if(width < height){
27936 scale = targetHeight / height;
27940 context.scale(scale, scale);
27942 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27943 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27945 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27946 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27948 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27949 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27951 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27956 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27957 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27959 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27960 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27962 var targetWidth = this.minWidth - 2 * x;
27963 var targetHeight = this.minHeight - 2 * y;
27967 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27968 scale = targetWidth / width;
27971 if(x > 0 && y == 0){
27972 scale = targetHeight / height;
27975 if(x > 0 && y > 0){
27976 scale = targetWidth / width;
27978 if(width < height){
27979 scale = targetHeight / height;
27983 context.scale(scale, scale);
27985 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27986 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27988 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27989 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27991 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27993 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28000 this.cropData = canvas.toDataURL(this.cropType);
28002 if(this.fireEvent('crop', this, this.cropData) !== false){
28003 this.process(this.file, this.cropData);
28010 setThumbBoxSize : function()
28014 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28015 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28016 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28018 this.minWidth = width;
28019 this.minHeight = height;
28021 if(this.rotate == 90 || this.rotate == 270){
28022 this.minWidth = height;
28023 this.minHeight = width;
28028 width = Math.ceil(this.minWidth * height / this.minHeight);
28030 if(this.minWidth > this.minHeight){
28032 height = Math.ceil(this.minHeight * width / this.minWidth);
28035 this.thumbEl.setStyle({
28036 width : width + 'px',
28037 height : height + 'px'
28044 setThumbBoxPosition : function()
28046 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28047 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28049 this.thumbEl.setLeft(x);
28050 this.thumbEl.setTop(y);
28054 baseRotateLevel : function()
28056 this.baseRotate = 1;
28059 typeof(this.exif) != 'undefined' &&
28060 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28061 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28063 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28066 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28070 baseScaleLevel : function()
28074 if(this.isDocument){
28076 if(this.baseRotate == 6 || this.baseRotate == 8){
28078 height = this.thumbEl.getHeight();
28079 this.baseScale = height / this.imageEl.OriginWidth;
28081 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28082 width = this.thumbEl.getWidth();
28083 this.baseScale = width / this.imageEl.OriginHeight;
28089 height = this.thumbEl.getHeight();
28090 this.baseScale = height / this.imageEl.OriginHeight;
28092 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28093 width = this.thumbEl.getWidth();
28094 this.baseScale = width / this.imageEl.OriginWidth;
28100 if(this.baseRotate == 6 || this.baseRotate == 8){
28102 width = this.thumbEl.getHeight();
28103 this.baseScale = width / this.imageEl.OriginHeight;
28105 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28106 height = this.thumbEl.getWidth();
28107 this.baseScale = height / this.imageEl.OriginHeight;
28110 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28111 height = this.thumbEl.getWidth();
28112 this.baseScale = height / this.imageEl.OriginHeight;
28114 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28115 width = this.thumbEl.getHeight();
28116 this.baseScale = width / this.imageEl.OriginWidth;
28123 width = this.thumbEl.getWidth();
28124 this.baseScale = width / this.imageEl.OriginWidth;
28126 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28127 height = this.thumbEl.getHeight();
28128 this.baseScale = height / this.imageEl.OriginHeight;
28131 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28133 height = this.thumbEl.getHeight();
28134 this.baseScale = height / this.imageEl.OriginHeight;
28136 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28137 width = this.thumbEl.getWidth();
28138 this.baseScale = width / this.imageEl.OriginWidth;
28146 getScaleLevel : function()
28148 return this.baseScale * Math.pow(1.1, this.scale);
28151 onTouchStart : function(e)
28153 if(!this.canvasLoaded){
28154 this.beforeSelectFile(e);
28158 var touches = e.browserEvent.touches;
28164 if(touches.length == 1){
28165 this.onMouseDown(e);
28169 if(touches.length != 2){
28175 for(var i = 0, finger; finger = touches[i]; i++){
28176 coords.push(finger.pageX, finger.pageY);
28179 var x = Math.pow(coords[0] - coords[2], 2);
28180 var y = Math.pow(coords[1] - coords[3], 2);
28182 this.startDistance = Math.sqrt(x + y);
28184 this.startScale = this.scale;
28186 this.pinching = true;
28187 this.dragable = false;
28191 onTouchMove : function(e)
28193 if(!this.pinching && !this.dragable){
28197 var touches = e.browserEvent.touches;
28204 this.onMouseMove(e);
28210 for(var i = 0, finger; finger = touches[i]; i++){
28211 coords.push(finger.pageX, finger.pageY);
28214 var x = Math.pow(coords[0] - coords[2], 2);
28215 var y = Math.pow(coords[1] - coords[3], 2);
28217 this.endDistance = Math.sqrt(x + y);
28219 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28221 if(!this.zoomable()){
28222 this.scale = this.startScale;
28230 onTouchEnd : function(e)
28232 this.pinching = false;
28233 this.dragable = false;
28237 process : function(file, crop)
28240 this.maskEl.mask(this.loadingText);
28243 this.xhr = new XMLHttpRequest();
28245 file.xhr = this.xhr;
28247 this.xhr.open(this.method, this.url, true);
28250 "Accept": "application/json",
28251 "Cache-Control": "no-cache",
28252 "X-Requested-With": "XMLHttpRequest"
28255 for (var headerName in headers) {
28256 var headerValue = headers[headerName];
28258 this.xhr.setRequestHeader(headerName, headerValue);
28264 this.xhr.onload = function()
28266 _this.xhrOnLoad(_this.xhr);
28269 this.xhr.onerror = function()
28271 _this.xhrOnError(_this.xhr);
28274 var formData = new FormData();
28276 formData.append('returnHTML', 'NO');
28279 formData.append('crop', crop);
28282 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28283 formData.append(this.paramName, file, file.name);
28286 if(typeof(file.filename) != 'undefined'){
28287 formData.append('filename', file.filename);
28290 if(typeof(file.mimetype) != 'undefined'){
28291 formData.append('mimetype', file.mimetype);
28294 if(this.fireEvent('arrange', this, formData) != false){
28295 this.xhr.send(formData);
28299 xhrOnLoad : function(xhr)
28302 this.maskEl.unmask();
28305 if (xhr.readyState !== 4) {
28306 this.fireEvent('exception', this, xhr);
28310 var response = Roo.decode(xhr.responseText);
28312 if(!response.success){
28313 this.fireEvent('exception', this, xhr);
28317 var response = Roo.decode(xhr.responseText);
28319 this.fireEvent('upload', this, response);
28323 xhrOnError : function()
28326 this.maskEl.unmask();
28329 Roo.log('xhr on error');
28331 var response = Roo.decode(xhr.responseText);
28337 prepare : function(file)
28340 this.maskEl.mask(this.loadingText);
28346 if(typeof(file) === 'string'){
28347 this.loadCanvas(file);
28351 if(!file || !this.urlAPI){
28356 this.cropType = file.type;
28360 if(this.fireEvent('prepare', this, this.file) != false){
28362 var reader = new FileReader();
28364 reader.onload = function (e) {
28365 if (e.target.error) {
28366 Roo.log(e.target.error);
28370 var buffer = e.target.result,
28371 dataView = new DataView(buffer),
28373 maxOffset = dataView.byteLength - 4,
28377 if (dataView.getUint16(0) === 0xffd8) {
28378 while (offset < maxOffset) {
28379 markerBytes = dataView.getUint16(offset);
28381 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28382 markerLength = dataView.getUint16(offset + 2) + 2;
28383 if (offset + markerLength > dataView.byteLength) {
28384 Roo.log('Invalid meta data: Invalid segment size.');
28388 if(markerBytes == 0xffe1){
28389 _this.parseExifData(
28396 offset += markerLength;
28406 var url = _this.urlAPI.createObjectURL(_this.file);
28408 _this.loadCanvas(url);
28413 reader.readAsArrayBuffer(this.file);
28419 parseExifData : function(dataView, offset, length)
28421 var tiffOffset = offset + 10,
28425 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28426 // No Exif data, might be XMP data instead
28430 // Check for the ASCII code for "Exif" (0x45786966):
28431 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28432 // No Exif data, might be XMP data instead
28435 if (tiffOffset + 8 > dataView.byteLength) {
28436 Roo.log('Invalid Exif data: Invalid segment size.');
28439 // Check for the two null bytes:
28440 if (dataView.getUint16(offset + 8) !== 0x0000) {
28441 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28444 // Check the byte alignment:
28445 switch (dataView.getUint16(tiffOffset)) {
28447 littleEndian = true;
28450 littleEndian = false;
28453 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28456 // Check for the TIFF tag marker (0x002A):
28457 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28458 Roo.log('Invalid Exif data: Missing TIFF marker.');
28461 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28462 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28464 this.parseExifTags(
28467 tiffOffset + dirOffset,
28472 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28477 if (dirOffset + 6 > dataView.byteLength) {
28478 Roo.log('Invalid Exif data: Invalid directory offset.');
28481 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28482 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28483 if (dirEndOffset + 4 > dataView.byteLength) {
28484 Roo.log('Invalid Exif data: Invalid directory size.');
28487 for (i = 0; i < tagsNumber; i += 1) {
28491 dirOffset + 2 + 12 * i, // tag offset
28495 // Return the offset to the next directory:
28496 return dataView.getUint32(dirEndOffset, littleEndian);
28499 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28501 var tag = dataView.getUint16(offset, littleEndian);
28503 this.exif[tag] = this.getExifValue(
28507 dataView.getUint16(offset + 2, littleEndian), // tag type
28508 dataView.getUint32(offset + 4, littleEndian), // tag length
28513 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28515 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28524 Roo.log('Invalid Exif data: Invalid tag type.');
28528 tagSize = tagType.size * length;
28529 // Determine if the value is contained in the dataOffset bytes,
28530 // or if the value at the dataOffset is a pointer to the actual data:
28531 dataOffset = tagSize > 4 ?
28532 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28533 if (dataOffset + tagSize > dataView.byteLength) {
28534 Roo.log('Invalid Exif data: Invalid data offset.');
28537 if (length === 1) {
28538 return tagType.getValue(dataView, dataOffset, littleEndian);
28541 for (i = 0; i < length; i += 1) {
28542 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28545 if (tagType.ascii) {
28547 // Concatenate the chars:
28548 for (i = 0; i < values.length; i += 1) {
28550 // Ignore the terminating NULL byte(s):
28551 if (c === '\u0000') {
28563 Roo.apply(Roo.bootstrap.UploadCropbox, {
28565 'Orientation': 0x0112
28569 1: 0, //'top-left',
28571 3: 180, //'bottom-right',
28572 // 4: 'bottom-left',
28574 6: 90, //'right-top',
28575 // 7: 'right-bottom',
28576 8: 270 //'left-bottom'
28580 // byte, 8-bit unsigned int:
28582 getValue: function (dataView, dataOffset) {
28583 return dataView.getUint8(dataOffset);
28587 // ascii, 8-bit byte:
28589 getValue: function (dataView, dataOffset) {
28590 return String.fromCharCode(dataView.getUint8(dataOffset));
28595 // short, 16 bit int:
28597 getValue: function (dataView, dataOffset, littleEndian) {
28598 return dataView.getUint16(dataOffset, littleEndian);
28602 // long, 32 bit int:
28604 getValue: function (dataView, dataOffset, littleEndian) {
28605 return dataView.getUint32(dataOffset, littleEndian);
28609 // rational = two long values, first is numerator, second is denominator:
28611 getValue: function (dataView, dataOffset, littleEndian) {
28612 return dataView.getUint32(dataOffset, littleEndian) /
28613 dataView.getUint32(dataOffset + 4, littleEndian);
28617 // slong, 32 bit signed int:
28619 getValue: function (dataView, dataOffset, littleEndian) {
28620 return dataView.getInt32(dataOffset, littleEndian);
28624 // srational, two slongs, first is numerator, second is denominator:
28626 getValue: function (dataView, dataOffset, littleEndian) {
28627 return dataView.getInt32(dataOffset, littleEndian) /
28628 dataView.getInt32(dataOffset + 4, littleEndian);
28638 cls : 'btn-group roo-upload-cropbox-rotate-left',
28639 action : 'rotate-left',
28643 cls : 'btn btn-default',
28644 html : '<i class="fa fa-undo"></i>'
28650 cls : 'btn-group roo-upload-cropbox-picture',
28651 action : 'picture',
28655 cls : 'btn btn-default',
28656 html : '<i class="fa fa-picture-o"></i>'
28662 cls : 'btn-group roo-upload-cropbox-rotate-right',
28663 action : 'rotate-right',
28667 cls : 'btn btn-default',
28668 html : '<i class="fa fa-repeat"></i>'
28676 cls : 'btn-group roo-upload-cropbox-rotate-left',
28677 action : 'rotate-left',
28681 cls : 'btn btn-default',
28682 html : '<i class="fa fa-undo"></i>'
28688 cls : 'btn-group roo-upload-cropbox-download',
28689 action : 'download',
28693 cls : 'btn btn-default',
28694 html : '<i class="fa fa-download"></i>'
28700 cls : 'btn-group roo-upload-cropbox-crop',
28705 cls : 'btn btn-default',
28706 html : '<i class="fa fa-crop"></i>'
28712 cls : 'btn-group roo-upload-cropbox-trash',
28717 cls : 'btn btn-default',
28718 html : '<i class="fa fa-trash"></i>'
28724 cls : 'btn-group roo-upload-cropbox-rotate-right',
28725 action : 'rotate-right',
28729 cls : 'btn btn-default',
28730 html : '<i class="fa fa-repeat"></i>'
28738 cls : 'btn-group roo-upload-cropbox-rotate-left',
28739 action : 'rotate-left',
28743 cls : 'btn btn-default',
28744 html : '<i class="fa fa-undo"></i>'
28750 cls : 'btn-group roo-upload-cropbox-rotate-right',
28751 action : 'rotate-right',
28755 cls : 'btn btn-default',
28756 html : '<i class="fa fa-repeat"></i>'
28769 * @class Roo.bootstrap.DocumentManager
28770 * @extends Roo.bootstrap.Component
28771 * Bootstrap DocumentManager class
28772 * @cfg {String} paramName default 'imageUpload'
28773 * @cfg {String} toolTipName default 'filename'
28774 * @cfg {String} method default POST
28775 * @cfg {String} url action url
28776 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28777 * @cfg {Boolean} multiple multiple upload default true
28778 * @cfg {Number} thumbSize default 300
28779 * @cfg {String} fieldLabel
28780 * @cfg {Number} labelWidth default 4
28781 * @cfg {String} labelAlign (left|top) default left
28782 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28783 * @cfg {Number} labellg set the width of label (1-12)
28784 * @cfg {Number} labelmd set the width of label (1-12)
28785 * @cfg {Number} labelsm set the width of label (1-12)
28786 * @cfg {Number} labelxs set the width of label (1-12)
28789 * Create a new DocumentManager
28790 * @param {Object} config The config object
28793 Roo.bootstrap.DocumentManager = function(config){
28794 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28797 this.delegates = [];
28802 * Fire when initial the DocumentManager
28803 * @param {Roo.bootstrap.DocumentManager} this
28808 * inspect selected file
28809 * @param {Roo.bootstrap.DocumentManager} this
28810 * @param {File} file
28815 * Fire when xhr load exception
28816 * @param {Roo.bootstrap.DocumentManager} this
28817 * @param {XMLHttpRequest} xhr
28819 "exception" : true,
28821 * @event afterupload
28822 * Fire when xhr load exception
28823 * @param {Roo.bootstrap.DocumentManager} this
28824 * @param {XMLHttpRequest} xhr
28826 "afterupload" : true,
28829 * prepare the form data
28830 * @param {Roo.bootstrap.DocumentManager} this
28831 * @param {Object} formData
28836 * Fire when remove the file
28837 * @param {Roo.bootstrap.DocumentManager} this
28838 * @param {Object} file
28843 * Fire after refresh the file
28844 * @param {Roo.bootstrap.DocumentManager} this
28849 * Fire after click the image
28850 * @param {Roo.bootstrap.DocumentManager} this
28851 * @param {Object} file
28856 * Fire when upload a image and editable set to true
28857 * @param {Roo.bootstrap.DocumentManager} this
28858 * @param {Object} file
28862 * @event beforeselectfile
28863 * Fire before select file
28864 * @param {Roo.bootstrap.DocumentManager} this
28866 "beforeselectfile" : true,
28869 * Fire before process file
28870 * @param {Roo.bootstrap.DocumentManager} this
28871 * @param {Object} file
28875 * @event previewrendered
28876 * Fire when preview rendered
28877 * @param {Roo.bootstrap.DocumentManager} this
28878 * @param {Object} file
28880 "previewrendered" : true,
28883 "previewResize" : true
28888 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28897 paramName : 'imageUpload',
28898 toolTipName : 'filename',
28901 labelAlign : 'left',
28911 getAutoCreate : function()
28913 var managerWidget = {
28915 cls : 'roo-document-manager',
28919 cls : 'roo-document-manager-selector',
28924 cls : 'roo-document-manager-uploader',
28928 cls : 'roo-document-manager-upload-btn',
28929 html : '<i class="fa fa-plus"></i>'
28940 cls : 'column col-md-12',
28945 if(this.fieldLabel.length){
28950 cls : 'column col-md-12',
28951 html : this.fieldLabel
28955 cls : 'column col-md-12',
28960 if(this.labelAlign == 'left'){
28965 html : this.fieldLabel
28974 if(this.labelWidth > 12){
28975 content[0].style = "width: " + this.labelWidth + 'px';
28978 if(this.labelWidth < 13 && this.labelmd == 0){
28979 this.labelmd = this.labelWidth;
28982 if(this.labellg > 0){
28983 content[0].cls += ' col-lg-' + this.labellg;
28984 content[1].cls += ' col-lg-' + (12 - this.labellg);
28987 if(this.labelmd > 0){
28988 content[0].cls += ' col-md-' + this.labelmd;
28989 content[1].cls += ' col-md-' + (12 - this.labelmd);
28992 if(this.labelsm > 0){
28993 content[0].cls += ' col-sm-' + this.labelsm;
28994 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28997 if(this.labelxs > 0){
28998 content[0].cls += ' col-xs-' + this.labelxs;
28999 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29007 cls : 'row clearfix',
29015 initEvents : function()
29017 this.managerEl = this.el.select('.roo-document-manager', true).first();
29018 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29020 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29021 this.selectorEl.hide();
29024 this.selectorEl.attr('multiple', 'multiple');
29027 this.selectorEl.on('change', this.onFileSelected, this);
29029 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29030 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29032 this.uploader.on('click', this.onUploaderClick, this);
29034 this.renderProgressDialog();
29038 window.addEventListener("resize", function() { _this.refresh(); } );
29040 this.fireEvent('initial', this);
29043 renderProgressDialog : function()
29047 this.progressDialog = new Roo.bootstrap.Modal({
29048 cls : 'roo-document-manager-progress-dialog',
29049 allow_close : false,
29059 btnclick : function() {
29060 _this.uploadCancel();
29066 this.progressDialog.render(Roo.get(document.body));
29068 this.progress = new Roo.bootstrap.Progress({
29069 cls : 'roo-document-manager-progress',
29074 this.progress.render(this.progressDialog.getChildContainer());
29076 this.progressBar = new Roo.bootstrap.ProgressBar({
29077 cls : 'roo-document-manager-progress-bar',
29080 aria_valuemax : 12,
29084 this.progressBar.render(this.progress.getChildContainer());
29087 onUploaderClick : function(e)
29089 e.preventDefault();
29091 if(this.fireEvent('beforeselectfile', this) != false){
29092 this.selectorEl.dom.click();
29097 onFileSelected : function(e)
29099 e.preventDefault();
29101 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29105 Roo.each(this.selectorEl.dom.files, function(file){
29106 if(this.fireEvent('inspect', this, file) != false){
29107 this.files.push(file);
29117 this.selectorEl.dom.value = '';
29119 if(!this.files || !this.files.length){
29123 if(this.boxes > 0 && this.files.length > this.boxes){
29124 this.files = this.files.slice(0, this.boxes);
29127 this.uploader.show();
29129 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29130 this.uploader.hide();
29139 Roo.each(this.files, function(file){
29141 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29142 var f = this.renderPreview(file);
29147 if(file.type.indexOf('image') != -1){
29148 this.delegates.push(
29150 _this.process(file);
29151 }).createDelegate(this)
29159 _this.process(file);
29160 }).createDelegate(this)
29165 this.files = files;
29167 this.delegates = this.delegates.concat(docs);
29169 if(!this.delegates.length){
29174 this.progressBar.aria_valuemax = this.delegates.length;
29181 arrange : function()
29183 if(!this.delegates.length){
29184 this.progressDialog.hide();
29189 var delegate = this.delegates.shift();
29191 this.progressDialog.show();
29193 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29195 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29200 refresh : function()
29202 this.uploader.show();
29204 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29205 this.uploader.hide();
29208 Roo.isTouch ? this.closable(false) : this.closable(true);
29210 this.fireEvent('refresh', this);
29213 onRemove : function(e, el, o)
29215 e.preventDefault();
29217 this.fireEvent('remove', this, o);
29221 remove : function(o)
29225 Roo.each(this.files, function(file){
29226 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29235 this.files = files;
29242 Roo.each(this.files, function(file){
29247 file.target.remove();
29256 onClick : function(e, el, o)
29258 e.preventDefault();
29260 this.fireEvent('click', this, o);
29264 closable : function(closable)
29266 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29268 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29280 xhrOnLoad : function(xhr)
29282 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29286 if (xhr.readyState !== 4) {
29288 this.fireEvent('exception', this, xhr);
29292 var response = Roo.decode(xhr.responseText);
29294 if(!response.success){
29296 this.fireEvent('exception', this, xhr);
29300 var file = this.renderPreview(response.data);
29302 this.files.push(file);
29306 this.fireEvent('afterupload', this, xhr);
29310 xhrOnError : function(xhr)
29312 Roo.log('xhr on error');
29314 var response = Roo.decode(xhr.responseText);
29321 process : function(file)
29323 if(this.fireEvent('process', this, file) !== false){
29324 if(this.editable && file.type.indexOf('image') != -1){
29325 this.fireEvent('edit', this, file);
29329 this.uploadStart(file, false);
29336 uploadStart : function(file, crop)
29338 this.xhr = new XMLHttpRequest();
29340 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29345 file.xhr = this.xhr;
29347 this.managerEl.createChild({
29349 cls : 'roo-document-manager-loading',
29353 tooltip : file.name,
29354 cls : 'roo-document-manager-thumb',
29355 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29361 this.xhr.open(this.method, this.url, true);
29364 "Accept": "application/json",
29365 "Cache-Control": "no-cache",
29366 "X-Requested-With": "XMLHttpRequest"
29369 for (var headerName in headers) {
29370 var headerValue = headers[headerName];
29372 this.xhr.setRequestHeader(headerName, headerValue);
29378 this.xhr.onload = function()
29380 _this.xhrOnLoad(_this.xhr);
29383 this.xhr.onerror = function()
29385 _this.xhrOnError(_this.xhr);
29388 var formData = new FormData();
29390 formData.append('returnHTML', 'NO');
29393 formData.append('crop', crop);
29396 formData.append(this.paramName, file, file.name);
29403 if(this.fireEvent('prepare', this, formData, options) != false){
29405 if(options.manually){
29409 this.xhr.send(formData);
29413 this.uploadCancel();
29416 uploadCancel : function()
29422 this.delegates = [];
29424 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29431 renderPreview : function(file)
29433 if(typeof(file.target) != 'undefined' && file.target){
29437 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29439 var previewEl = this.managerEl.createChild({
29441 cls : 'roo-document-manager-preview',
29445 tooltip : file[this.toolTipName],
29446 cls : 'roo-document-manager-thumb',
29447 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29452 html : '<i class="fa fa-times-circle"></i>'
29457 var close = previewEl.select('button.close', true).first();
29459 close.on('click', this.onRemove, this, file);
29461 file.target = previewEl;
29463 var image = previewEl.select('img', true).first();
29467 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29469 image.on('click', this.onClick, this, file);
29471 this.fireEvent('previewrendered', this, file);
29477 onPreviewLoad : function(file, image)
29479 if(typeof(file.target) == 'undefined' || !file.target){
29483 var width = image.dom.naturalWidth || image.dom.width;
29484 var height = image.dom.naturalHeight || image.dom.height;
29486 if(!this.previewResize) {
29490 if(width > height){
29491 file.target.addClass('wide');
29495 file.target.addClass('tall');
29500 uploadFromSource : function(file, crop)
29502 this.xhr = new XMLHttpRequest();
29504 this.managerEl.createChild({
29506 cls : 'roo-document-manager-loading',
29510 tooltip : file.name,
29511 cls : 'roo-document-manager-thumb',
29512 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29518 this.xhr.open(this.method, this.url, true);
29521 "Accept": "application/json",
29522 "Cache-Control": "no-cache",
29523 "X-Requested-With": "XMLHttpRequest"
29526 for (var headerName in headers) {
29527 var headerValue = headers[headerName];
29529 this.xhr.setRequestHeader(headerName, headerValue);
29535 this.xhr.onload = function()
29537 _this.xhrOnLoad(_this.xhr);
29540 this.xhr.onerror = function()
29542 _this.xhrOnError(_this.xhr);
29545 var formData = new FormData();
29547 formData.append('returnHTML', 'NO');
29549 formData.append('crop', crop);
29551 if(typeof(file.filename) != 'undefined'){
29552 formData.append('filename', file.filename);
29555 if(typeof(file.mimetype) != 'undefined'){
29556 formData.append('mimetype', file.mimetype);
29561 if(this.fireEvent('prepare', this, formData) != false){
29562 this.xhr.send(formData);
29572 * @class Roo.bootstrap.DocumentViewer
29573 * @extends Roo.bootstrap.Component
29574 * Bootstrap DocumentViewer class
29575 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29576 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29579 * Create a new DocumentViewer
29580 * @param {Object} config The config object
29583 Roo.bootstrap.DocumentViewer = function(config){
29584 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29589 * Fire after initEvent
29590 * @param {Roo.bootstrap.DocumentViewer} this
29596 * @param {Roo.bootstrap.DocumentViewer} this
29601 * Fire after download button
29602 * @param {Roo.bootstrap.DocumentViewer} this
29607 * Fire after trash button
29608 * @param {Roo.bootstrap.DocumentViewer} this
29615 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29617 showDownload : true,
29621 getAutoCreate : function()
29625 cls : 'roo-document-viewer',
29629 cls : 'roo-document-viewer-body',
29633 cls : 'roo-document-viewer-thumb',
29637 cls : 'roo-document-viewer-image'
29645 cls : 'roo-document-viewer-footer',
29648 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29652 cls : 'btn-group roo-document-viewer-download',
29656 cls : 'btn btn-default',
29657 html : '<i class="fa fa-download"></i>'
29663 cls : 'btn-group roo-document-viewer-trash',
29667 cls : 'btn btn-default',
29668 html : '<i class="fa fa-trash"></i>'
29681 initEvents : function()
29683 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29684 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29686 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29687 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29689 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29690 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29692 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29693 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29695 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29696 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29698 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29699 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29701 this.bodyEl.on('click', this.onClick, this);
29702 this.downloadBtn.on('click', this.onDownload, this);
29703 this.trashBtn.on('click', this.onTrash, this);
29705 this.downloadBtn.hide();
29706 this.trashBtn.hide();
29708 if(this.showDownload){
29709 this.downloadBtn.show();
29712 if(this.showTrash){
29713 this.trashBtn.show();
29716 if(!this.showDownload && !this.showTrash) {
29717 this.footerEl.hide();
29722 initial : function()
29724 this.fireEvent('initial', this);
29728 onClick : function(e)
29730 e.preventDefault();
29732 this.fireEvent('click', this);
29735 onDownload : function(e)
29737 e.preventDefault();
29739 this.fireEvent('download', this);
29742 onTrash : function(e)
29744 e.preventDefault();
29746 this.fireEvent('trash', this);
29758 * @class Roo.bootstrap.NavProgressBar
29759 * @extends Roo.bootstrap.Component
29760 * Bootstrap NavProgressBar class
29763 * Create a new nav progress bar
29764 * @param {Object} config The config object
29767 Roo.bootstrap.NavProgressBar = function(config){
29768 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29770 this.bullets = this.bullets || [];
29772 // Roo.bootstrap.NavProgressBar.register(this);
29776 * Fires when the active item changes
29777 * @param {Roo.bootstrap.NavProgressBar} this
29778 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29779 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29786 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29791 getAutoCreate : function()
29793 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29797 cls : 'roo-navigation-bar-group',
29801 cls : 'roo-navigation-top-bar'
29805 cls : 'roo-navigation-bullets-bar',
29809 cls : 'roo-navigation-bar'
29816 cls : 'roo-navigation-bottom-bar'
29826 initEvents: function()
29831 onRender : function(ct, position)
29833 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29835 if(this.bullets.length){
29836 Roo.each(this.bullets, function(b){
29845 addItem : function(cfg)
29847 var item = new Roo.bootstrap.NavProgressItem(cfg);
29849 item.parentId = this.id;
29850 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29853 var top = new Roo.bootstrap.Element({
29855 cls : 'roo-navigation-bar-text'
29858 var bottom = new Roo.bootstrap.Element({
29860 cls : 'roo-navigation-bar-text'
29863 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29864 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29866 var topText = new Roo.bootstrap.Element({
29868 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29871 var bottomText = new Roo.bootstrap.Element({
29873 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29876 topText.onRender(top.el, null);
29877 bottomText.onRender(bottom.el, null);
29880 item.bottomEl = bottom;
29883 this.barItems.push(item);
29888 getActive : function()
29890 var active = false;
29892 Roo.each(this.barItems, function(v){
29894 if (!v.isActive()) {
29906 setActiveItem : function(item)
29910 Roo.each(this.barItems, function(v){
29911 if (v.rid == item.rid) {
29915 if (v.isActive()) {
29916 v.setActive(false);
29921 item.setActive(true);
29923 this.fireEvent('changed', this, item, prev);
29926 getBarItem: function(rid)
29930 Roo.each(this.barItems, function(e) {
29931 if (e.rid != rid) {
29942 indexOfItem : function(item)
29946 Roo.each(this.barItems, function(v, i){
29948 if (v.rid != item.rid) {
29959 setActiveNext : function()
29961 var i = this.indexOfItem(this.getActive());
29963 if (i > this.barItems.length) {
29967 this.setActiveItem(this.barItems[i+1]);
29970 setActivePrev : function()
29972 var i = this.indexOfItem(this.getActive());
29978 this.setActiveItem(this.barItems[i-1]);
29981 format : function()
29983 if(!this.barItems.length){
29987 var width = 100 / this.barItems.length;
29989 Roo.each(this.barItems, function(i){
29990 i.el.setStyle('width', width + '%');
29991 i.topEl.el.setStyle('width', width + '%');
29992 i.bottomEl.el.setStyle('width', width + '%');
30001 * Nav Progress Item
30006 * @class Roo.bootstrap.NavProgressItem
30007 * @extends Roo.bootstrap.Component
30008 * Bootstrap NavProgressItem class
30009 * @cfg {String} rid the reference id
30010 * @cfg {Boolean} active (true|false) Is item active default false
30011 * @cfg {Boolean} disabled (true|false) Is item active default false
30012 * @cfg {String} html
30013 * @cfg {String} position (top|bottom) text position default bottom
30014 * @cfg {String} icon show icon instead of number
30017 * Create a new NavProgressItem
30018 * @param {Object} config The config object
30020 Roo.bootstrap.NavProgressItem = function(config){
30021 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30026 * The raw click event for the entire grid.
30027 * @param {Roo.bootstrap.NavProgressItem} this
30028 * @param {Roo.EventObject} e
30035 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30041 position : 'bottom',
30044 getAutoCreate : function()
30046 var iconCls = 'roo-navigation-bar-item-icon';
30048 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30052 cls: 'roo-navigation-bar-item',
30062 cfg.cls += ' active';
30065 cfg.cls += ' disabled';
30071 disable : function()
30073 this.setDisabled(true);
30076 enable : function()
30078 this.setDisabled(false);
30081 initEvents: function()
30083 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30085 this.iconEl.on('click', this.onClick, this);
30088 onClick : function(e)
30090 e.preventDefault();
30096 if(this.fireEvent('click', this, e) === false){
30100 this.parent().setActiveItem(this);
30103 isActive: function ()
30105 return this.active;
30108 setActive : function(state)
30110 if(this.active == state){
30114 this.active = state;
30117 this.el.addClass('active');
30121 this.el.removeClass('active');
30126 setDisabled : function(state)
30128 if(this.disabled == state){
30132 this.disabled = state;
30135 this.el.addClass('disabled');
30139 this.el.removeClass('disabled');
30142 tooltipEl : function()
30144 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30157 * @class Roo.bootstrap.FieldLabel
30158 * @extends Roo.bootstrap.Component
30159 * Bootstrap FieldLabel class
30160 * @cfg {String} html contents of the element
30161 * @cfg {String} tag tag of the element default label
30162 * @cfg {String} cls class of the element
30163 * @cfg {String} target label target
30164 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30165 * @cfg {String} invalidClass default "text-warning"
30166 * @cfg {String} validClass default "text-success"
30167 * @cfg {String} iconTooltip default "This field is required"
30168 * @cfg {String} indicatorpos (left|right) default left
30171 * Create a new FieldLabel
30172 * @param {Object} config The config object
30175 Roo.bootstrap.FieldLabel = function(config){
30176 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30181 * Fires after the field has been marked as invalid.
30182 * @param {Roo.form.FieldLabel} this
30183 * @param {String} msg The validation message
30188 * Fires after the field has been validated with no errors.
30189 * @param {Roo.form.FieldLabel} this
30195 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30202 invalidClass : 'has-warning',
30203 validClass : 'has-success',
30204 iconTooltip : 'This field is required',
30205 indicatorpos : 'left',
30207 getAutoCreate : function(){
30210 if (!this.allowBlank) {
30216 cls : 'roo-bootstrap-field-label ' + this.cls,
30221 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30222 tooltip : this.iconTooltip
30231 if(this.indicatorpos == 'right'){
30234 cls : 'roo-bootstrap-field-label ' + this.cls,
30243 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30244 tooltip : this.iconTooltip
30253 initEvents: function()
30255 Roo.bootstrap.Element.superclass.initEvents.call(this);
30257 this.indicator = this.indicatorEl();
30259 if(this.indicator){
30260 this.indicator.removeClass('visible');
30261 this.indicator.addClass('invisible');
30264 Roo.bootstrap.FieldLabel.register(this);
30267 indicatorEl : function()
30269 var indicator = this.el.select('i.roo-required-indicator',true).first();
30280 * Mark this field as valid
30282 markValid : function()
30284 if(this.indicator){
30285 this.indicator.removeClass('visible');
30286 this.indicator.addClass('invisible');
30289 this.el.removeClass(this.invalidClass);
30291 this.el.addClass(this.validClass);
30293 this.fireEvent('valid', this);
30297 * Mark this field as invalid
30298 * @param {String} msg The validation message
30300 markInvalid : function(msg)
30302 if(this.indicator){
30303 this.indicator.removeClass('invisible');
30304 this.indicator.addClass('visible');
30307 this.el.removeClass(this.validClass);
30309 this.el.addClass(this.invalidClass);
30311 this.fireEvent('invalid', this, msg);
30317 Roo.apply(Roo.bootstrap.FieldLabel, {
30322 * register a FieldLabel Group
30323 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30325 register : function(label)
30327 if(this.groups.hasOwnProperty(label.target)){
30331 this.groups[label.target] = label;
30335 * fetch a FieldLabel Group based on the target
30336 * @param {string} target
30337 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30339 get: function(target) {
30340 if (typeof(this.groups[target]) == 'undefined') {
30344 return this.groups[target] ;
30353 * page DateSplitField.
30359 * @class Roo.bootstrap.DateSplitField
30360 * @extends Roo.bootstrap.Component
30361 * Bootstrap DateSplitField class
30362 * @cfg {string} fieldLabel - the label associated
30363 * @cfg {Number} labelWidth set the width of label (0-12)
30364 * @cfg {String} labelAlign (top|left)
30365 * @cfg {Boolean} dayAllowBlank (true|false) default false
30366 * @cfg {Boolean} monthAllowBlank (true|false) default false
30367 * @cfg {Boolean} yearAllowBlank (true|false) default false
30368 * @cfg {string} dayPlaceholder
30369 * @cfg {string} monthPlaceholder
30370 * @cfg {string} yearPlaceholder
30371 * @cfg {string} dayFormat default 'd'
30372 * @cfg {string} monthFormat default 'm'
30373 * @cfg {string} yearFormat default 'Y'
30374 * @cfg {Number} labellg set the width of label (1-12)
30375 * @cfg {Number} labelmd set the width of label (1-12)
30376 * @cfg {Number} labelsm set the width of label (1-12)
30377 * @cfg {Number} labelxs set the width of label (1-12)
30381 * Create a new DateSplitField
30382 * @param {Object} config The config object
30385 Roo.bootstrap.DateSplitField = function(config){
30386 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30392 * getting the data of years
30393 * @param {Roo.bootstrap.DateSplitField} this
30394 * @param {Object} years
30399 * getting the data of days
30400 * @param {Roo.bootstrap.DateSplitField} this
30401 * @param {Object} days
30406 * Fires after the field has been marked as invalid.
30407 * @param {Roo.form.Field} this
30408 * @param {String} msg The validation message
30413 * Fires after the field has been validated with no errors.
30414 * @param {Roo.form.Field} this
30420 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30423 labelAlign : 'top',
30425 dayAllowBlank : false,
30426 monthAllowBlank : false,
30427 yearAllowBlank : false,
30428 dayPlaceholder : '',
30429 monthPlaceholder : '',
30430 yearPlaceholder : '',
30434 isFormField : true,
30440 getAutoCreate : function()
30444 cls : 'row roo-date-split-field-group',
30449 cls : 'form-hidden-field roo-date-split-field-group-value',
30455 var labelCls = 'col-md-12';
30456 var contentCls = 'col-md-4';
30458 if(this.fieldLabel){
30462 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30466 html : this.fieldLabel
30471 if(this.labelAlign == 'left'){
30473 if(this.labelWidth > 12){
30474 label.style = "width: " + this.labelWidth + 'px';
30477 if(this.labelWidth < 13 && this.labelmd == 0){
30478 this.labelmd = this.labelWidth;
30481 if(this.labellg > 0){
30482 labelCls = ' col-lg-' + this.labellg;
30483 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30486 if(this.labelmd > 0){
30487 labelCls = ' col-md-' + this.labelmd;
30488 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30491 if(this.labelsm > 0){
30492 labelCls = ' col-sm-' + this.labelsm;
30493 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30496 if(this.labelxs > 0){
30497 labelCls = ' col-xs-' + this.labelxs;
30498 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30502 label.cls += ' ' + labelCls;
30504 cfg.cn.push(label);
30507 Roo.each(['day', 'month', 'year'], function(t){
30510 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30517 inputEl: function ()
30519 return this.el.select('.roo-date-split-field-group-value', true).first();
30522 onRender : function(ct, position)
30526 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30528 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30530 this.dayField = new Roo.bootstrap.ComboBox({
30531 allowBlank : this.dayAllowBlank,
30532 alwaysQuery : true,
30533 displayField : 'value',
30536 forceSelection : true,
30538 placeholder : this.dayPlaceholder,
30539 selectOnFocus : true,
30540 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30541 triggerAction : 'all',
30543 valueField : 'value',
30544 store : new Roo.data.SimpleStore({
30545 data : (function() {
30547 _this.fireEvent('days', _this, days);
30550 fields : [ 'value' ]
30553 select : function (_self, record, index)
30555 _this.setValue(_this.getValue());
30560 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30562 this.monthField = new Roo.bootstrap.MonthField({
30563 after : '<i class=\"fa fa-calendar\"></i>',
30564 allowBlank : this.monthAllowBlank,
30565 placeholder : this.monthPlaceholder,
30568 render : function (_self)
30570 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30571 e.preventDefault();
30575 select : function (_self, oldvalue, newvalue)
30577 _this.setValue(_this.getValue());
30582 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30584 this.yearField = new Roo.bootstrap.ComboBox({
30585 allowBlank : this.yearAllowBlank,
30586 alwaysQuery : true,
30587 displayField : 'value',
30590 forceSelection : true,
30592 placeholder : this.yearPlaceholder,
30593 selectOnFocus : true,
30594 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30595 triggerAction : 'all',
30597 valueField : 'value',
30598 store : new Roo.data.SimpleStore({
30599 data : (function() {
30601 _this.fireEvent('years', _this, years);
30604 fields : [ 'value' ]
30607 select : function (_self, record, index)
30609 _this.setValue(_this.getValue());
30614 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30617 setValue : function(v, format)
30619 this.inputEl.dom.value = v;
30621 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30623 var d = Date.parseDate(v, f);
30630 this.setDay(d.format(this.dayFormat));
30631 this.setMonth(d.format(this.monthFormat));
30632 this.setYear(d.format(this.yearFormat));
30639 setDay : function(v)
30641 this.dayField.setValue(v);
30642 this.inputEl.dom.value = this.getValue();
30647 setMonth : function(v)
30649 this.monthField.setValue(v, true);
30650 this.inputEl.dom.value = this.getValue();
30655 setYear : function(v)
30657 this.yearField.setValue(v);
30658 this.inputEl.dom.value = this.getValue();
30663 getDay : function()
30665 return this.dayField.getValue();
30668 getMonth : function()
30670 return this.monthField.getValue();
30673 getYear : function()
30675 return this.yearField.getValue();
30678 getValue : function()
30680 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30682 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30692 this.inputEl.dom.value = '';
30697 validate : function()
30699 var d = this.dayField.validate();
30700 var m = this.monthField.validate();
30701 var y = this.yearField.validate();
30706 (!this.dayAllowBlank && !d) ||
30707 (!this.monthAllowBlank && !m) ||
30708 (!this.yearAllowBlank && !y)
30713 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30722 this.markInvalid();
30727 markValid : function()
30730 var label = this.el.select('label', true).first();
30731 var icon = this.el.select('i.fa-star', true).first();
30737 this.fireEvent('valid', this);
30741 * Mark this field as invalid
30742 * @param {String} msg The validation message
30744 markInvalid : function(msg)
30747 var label = this.el.select('label', true).first();
30748 var icon = this.el.select('i.fa-star', true).first();
30750 if(label && !icon){
30751 this.el.select('.roo-date-split-field-label', true).createChild({
30753 cls : 'text-danger fa fa-lg fa-star',
30754 tooltip : 'This field is required',
30755 style : 'margin-right:5px;'
30759 this.fireEvent('invalid', this, msg);
30762 clearInvalid : function()
30764 var label = this.el.select('label', true).first();
30765 var icon = this.el.select('i.fa-star', true).first();
30771 this.fireEvent('valid', this);
30774 getName: function()
30784 * http://masonry.desandro.com
30786 * The idea is to render all the bricks based on vertical width...
30788 * The original code extends 'outlayer' - we might need to use that....
30794 * @class Roo.bootstrap.LayoutMasonry
30795 * @extends Roo.bootstrap.Component
30796 * Bootstrap Layout Masonry class
30799 * Create a new Element
30800 * @param {Object} config The config object
30803 Roo.bootstrap.LayoutMasonry = function(config){
30805 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30809 Roo.bootstrap.LayoutMasonry.register(this);
30815 * Fire after layout the items
30816 * @param {Roo.bootstrap.LayoutMasonry} this
30817 * @param {Roo.EventObject} e
30824 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30827 * @cfg {Boolean} isLayoutInstant = no animation?
30829 isLayoutInstant : false, // needed?
30832 * @cfg {Number} boxWidth width of the columns
30837 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30842 * @cfg {Number} padWidth padding below box..
30847 * @cfg {Number} gutter gutter width..
30852 * @cfg {Number} maxCols maximum number of columns
30858 * @cfg {Boolean} isAutoInitial defalut true
30860 isAutoInitial : true,
30865 * @cfg {Boolean} isHorizontal defalut false
30867 isHorizontal : false,
30869 currentSize : null,
30875 bricks: null, //CompositeElement
30879 _isLayoutInited : false,
30881 // isAlternative : false, // only use for vertical layout...
30884 * @cfg {Number} alternativePadWidth padding below box..
30886 alternativePadWidth : 50,
30888 selectedBrick : [],
30890 getAutoCreate : function(){
30892 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30896 cls: 'blog-masonary-wrapper ' + this.cls,
30898 cls : 'mas-boxes masonary'
30905 getChildContainer: function( )
30907 if (this.boxesEl) {
30908 return this.boxesEl;
30911 this.boxesEl = this.el.select('.mas-boxes').first();
30913 return this.boxesEl;
30917 initEvents : function()
30921 if(this.isAutoInitial){
30922 Roo.log('hook children rendered');
30923 this.on('childrenrendered', function() {
30924 Roo.log('children rendered');
30930 initial : function()
30932 this.selectedBrick = [];
30934 this.currentSize = this.el.getBox(true);
30936 Roo.EventManager.onWindowResize(this.resize, this);
30938 if(!this.isAutoInitial){
30946 //this.layout.defer(500,this);
30950 resize : function()
30952 var cs = this.el.getBox(true);
30955 this.currentSize.width == cs.width &&
30956 this.currentSize.x == cs.x &&
30957 this.currentSize.height == cs.height &&
30958 this.currentSize.y == cs.y
30960 Roo.log("no change in with or X or Y");
30964 this.currentSize = cs;
30970 layout : function()
30972 this._resetLayout();
30974 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30976 this.layoutItems( isInstant );
30978 this._isLayoutInited = true;
30980 this.fireEvent('layout', this);
30984 _resetLayout : function()
30986 if(this.isHorizontal){
30987 this.horizontalMeasureColumns();
30991 this.verticalMeasureColumns();
30995 verticalMeasureColumns : function()
30997 this.getContainerWidth();
30999 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31000 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31004 var boxWidth = this.boxWidth + this.padWidth;
31006 if(this.containerWidth < this.boxWidth){
31007 boxWidth = this.containerWidth
31010 var containerWidth = this.containerWidth;
31012 var cols = Math.floor(containerWidth / boxWidth);
31014 this.cols = Math.max( cols, 1 );
31016 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31018 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31020 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31022 this.colWidth = boxWidth + avail - this.padWidth;
31024 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31025 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31028 horizontalMeasureColumns : function()
31030 this.getContainerWidth();
31032 var boxWidth = this.boxWidth;
31034 if(this.containerWidth < boxWidth){
31035 boxWidth = this.containerWidth;
31038 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31040 this.el.setHeight(boxWidth);
31044 getContainerWidth : function()
31046 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31049 layoutItems : function( isInstant )
31051 Roo.log(this.bricks);
31053 var items = Roo.apply([], this.bricks);
31055 if(this.isHorizontal){
31056 this._horizontalLayoutItems( items , isInstant );
31060 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31061 // this._verticalAlternativeLayoutItems( items , isInstant );
31065 this._verticalLayoutItems( items , isInstant );
31069 _verticalLayoutItems : function ( items , isInstant)
31071 if ( !items || !items.length ) {
31076 ['xs', 'xs', 'xs', 'tall'],
31077 ['xs', 'xs', 'tall'],
31078 ['xs', 'xs', 'sm'],
31079 ['xs', 'xs', 'xs'],
31085 ['sm', 'xs', 'xs'],
31089 ['tall', 'xs', 'xs', 'xs'],
31090 ['tall', 'xs', 'xs'],
31102 Roo.each(items, function(item, k){
31104 switch (item.size) {
31105 // these layouts take up a full box,
31116 boxes.push([item]);
31139 var filterPattern = function(box, length)
31147 var pattern = box.slice(0, length);
31151 Roo.each(pattern, function(i){
31152 format.push(i.size);
31155 Roo.each(standard, function(s){
31157 if(String(s) != String(format)){
31166 if(!match && length == 1){
31171 filterPattern(box, length - 1);
31175 queue.push(pattern);
31177 box = box.slice(length, box.length);
31179 filterPattern(box, 4);
31185 Roo.each(boxes, function(box, k){
31191 if(box.length == 1){
31196 filterPattern(box, 4);
31200 this._processVerticalLayoutQueue( queue, isInstant );
31204 // _verticalAlternativeLayoutItems : function( items , isInstant )
31206 // if ( !items || !items.length ) {
31210 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31214 _horizontalLayoutItems : function ( items , isInstant)
31216 if ( !items || !items.length || items.length < 3) {
31222 var eItems = items.slice(0, 3);
31224 items = items.slice(3, items.length);
31227 ['xs', 'xs', 'xs', 'wide'],
31228 ['xs', 'xs', 'wide'],
31229 ['xs', 'xs', 'sm'],
31230 ['xs', 'xs', 'xs'],
31236 ['sm', 'xs', 'xs'],
31240 ['wide', 'xs', 'xs', 'xs'],
31241 ['wide', 'xs', 'xs'],
31254 Roo.each(items, function(item, k){
31256 switch (item.size) {
31267 boxes.push([item]);
31291 var filterPattern = function(box, length)
31299 var pattern = box.slice(0, length);
31303 Roo.each(pattern, function(i){
31304 format.push(i.size);
31307 Roo.each(standard, function(s){
31309 if(String(s) != String(format)){
31318 if(!match && length == 1){
31323 filterPattern(box, length - 1);
31327 queue.push(pattern);
31329 box = box.slice(length, box.length);
31331 filterPattern(box, 4);
31337 Roo.each(boxes, function(box, k){
31343 if(box.length == 1){
31348 filterPattern(box, 4);
31355 var pos = this.el.getBox(true);
31359 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31361 var hit_end = false;
31363 Roo.each(queue, function(box){
31367 Roo.each(box, function(b){
31369 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31379 Roo.each(box, function(b){
31381 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31384 mx = Math.max(mx, b.x);
31388 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31392 Roo.each(box, function(b){
31394 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31408 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31411 /** Sets position of item in DOM
31412 * @param {Element} item
31413 * @param {Number} x - horizontal position
31414 * @param {Number} y - vertical position
31415 * @param {Boolean} isInstant - disables transitions
31417 _processVerticalLayoutQueue : function( queue, isInstant )
31419 var pos = this.el.getBox(true);
31424 for (var i = 0; i < this.cols; i++){
31428 Roo.each(queue, function(box, k){
31430 var col = k % this.cols;
31432 Roo.each(box, function(b,kk){
31434 b.el.position('absolute');
31436 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31437 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31439 if(b.size == 'md-left' || b.size == 'md-right'){
31440 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31441 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31444 b.el.setWidth(width);
31445 b.el.setHeight(height);
31447 b.el.select('iframe',true).setSize(width,height);
31451 for (var i = 0; i < this.cols; i++){
31453 if(maxY[i] < maxY[col]){
31458 col = Math.min(col, i);
31462 x = pos.x + col * (this.colWidth + this.padWidth);
31466 var positions = [];
31468 switch (box.length){
31470 positions = this.getVerticalOneBoxColPositions(x, y, box);
31473 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31476 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31479 positions = this.getVerticalFourBoxColPositions(x, y, box);
31485 Roo.each(box, function(b,kk){
31487 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31489 var sz = b.el.getSize();
31491 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31499 for (var i = 0; i < this.cols; i++){
31500 mY = Math.max(mY, maxY[i]);
31503 this.el.setHeight(mY - pos.y);
31507 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31509 // var pos = this.el.getBox(true);
31512 // var maxX = pos.right;
31514 // var maxHeight = 0;
31516 // Roo.each(items, function(item, k){
31520 // item.el.position('absolute');
31522 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31524 // item.el.setWidth(width);
31526 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31528 // item.el.setHeight(height);
31531 // item.el.setXY([x, y], isInstant ? false : true);
31533 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31536 // y = y + height + this.alternativePadWidth;
31538 // maxHeight = maxHeight + height + this.alternativePadWidth;
31542 // this.el.setHeight(maxHeight);
31546 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31548 var pos = this.el.getBox(true);
31553 var maxX = pos.right;
31555 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31557 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31559 Roo.each(queue, function(box, k){
31561 Roo.each(box, function(b, kk){
31563 b.el.position('absolute');
31565 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31566 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31568 if(b.size == 'md-left' || b.size == 'md-right'){
31569 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31570 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31573 b.el.setWidth(width);
31574 b.el.setHeight(height);
31582 var positions = [];
31584 switch (box.length){
31586 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31589 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31592 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31595 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31601 Roo.each(box, function(b,kk){
31603 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31605 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31613 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31615 Roo.each(eItems, function(b,k){
31617 b.size = (k == 0) ? 'sm' : 'xs';
31618 b.x = (k == 0) ? 2 : 1;
31619 b.y = (k == 0) ? 2 : 1;
31621 b.el.position('absolute');
31623 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31625 b.el.setWidth(width);
31627 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31629 b.el.setHeight(height);
31633 var positions = [];
31636 x : maxX - this.unitWidth * 2 - this.gutter,
31641 x : maxX - this.unitWidth,
31642 y : minY + (this.unitWidth + this.gutter) * 2
31646 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31650 Roo.each(eItems, function(b,k){
31652 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31658 getVerticalOneBoxColPositions : function(x, y, box)
31662 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31664 if(box[0].size == 'md-left'){
31668 if(box[0].size == 'md-right'){
31673 x : x + (this.unitWidth + this.gutter) * rand,
31680 getVerticalTwoBoxColPositions : function(x, y, box)
31684 if(box[0].size == 'xs'){
31688 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31692 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31706 x : x + (this.unitWidth + this.gutter) * 2,
31707 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31714 getVerticalThreeBoxColPositions : function(x, y, box)
31718 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31726 x : x + (this.unitWidth + this.gutter) * 1,
31731 x : x + (this.unitWidth + this.gutter) * 2,
31739 if(box[0].size == 'xs' && box[1].size == 'xs'){
31748 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31752 x : x + (this.unitWidth + this.gutter) * 1,
31766 x : x + (this.unitWidth + this.gutter) * 2,
31771 x : x + (this.unitWidth + this.gutter) * 2,
31772 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31779 getVerticalFourBoxColPositions : function(x, y, box)
31783 if(box[0].size == 'xs'){
31792 y : y + (this.unitHeight + this.gutter) * 1
31797 y : y + (this.unitHeight + this.gutter) * 2
31801 x : x + (this.unitWidth + this.gutter) * 1,
31815 x : x + (this.unitWidth + this.gutter) * 2,
31820 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31821 y : y + (this.unitHeight + this.gutter) * 1
31825 x : x + (this.unitWidth + this.gutter) * 2,
31826 y : y + (this.unitWidth + this.gutter) * 2
31833 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31837 if(box[0].size == 'md-left'){
31839 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31846 if(box[0].size == 'md-right'){
31848 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31849 y : minY + (this.unitWidth + this.gutter) * 1
31855 var rand = Math.floor(Math.random() * (4 - box[0].y));
31858 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31859 y : minY + (this.unitWidth + this.gutter) * rand
31866 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31870 if(box[0].size == 'xs'){
31873 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31878 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31879 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31887 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31892 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31893 y : minY + (this.unitWidth + this.gutter) * 2
31900 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31904 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31907 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31912 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31913 y : minY + (this.unitWidth + this.gutter) * 1
31917 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31918 y : minY + (this.unitWidth + this.gutter) * 2
31925 if(box[0].size == 'xs' && box[1].size == 'xs'){
31928 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31933 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31938 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31939 y : minY + (this.unitWidth + this.gutter) * 1
31947 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31952 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31953 y : minY + (this.unitWidth + this.gutter) * 2
31957 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31958 y : minY + (this.unitWidth + this.gutter) * 2
31965 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31969 if(box[0].size == 'xs'){
31972 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31977 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31982 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),
31987 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31988 y : minY + (this.unitWidth + this.gutter) * 1
31996 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32001 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32002 y : minY + (this.unitWidth + this.gutter) * 2
32006 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32007 y : minY + (this.unitWidth + this.gutter) * 2
32011 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),
32012 y : minY + (this.unitWidth + this.gutter) * 2
32020 * remove a Masonry Brick
32021 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32023 removeBrick : function(brick_id)
32029 for (var i = 0; i<this.bricks.length; i++) {
32030 if (this.bricks[i].id == brick_id) {
32031 this.bricks.splice(i,1);
32032 this.el.dom.removeChild(Roo.get(brick_id).dom);
32039 * adds a Masonry Brick
32040 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32042 addBrick : function(cfg)
32044 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32045 //this.register(cn);
32046 cn.parentId = this.id;
32047 cn.render(this.el);
32052 * register a Masonry Brick
32053 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32056 register : function(brick)
32058 this.bricks.push(brick);
32059 brick.masonryId = this.id;
32063 * clear all the Masonry Brick
32065 clearAll : function()
32068 //this.getChildContainer().dom.innerHTML = "";
32069 this.el.dom.innerHTML = '';
32072 getSelected : function()
32074 if (!this.selectedBrick) {
32078 return this.selectedBrick;
32082 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32086 * register a Masonry Layout
32087 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32090 register : function(layout)
32092 this.groups[layout.id] = layout;
32095 * fetch a Masonry Layout based on the masonry layout ID
32096 * @param {string} the masonry layout to add
32097 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32100 get: function(layout_id) {
32101 if (typeof(this.groups[layout_id]) == 'undefined') {
32104 return this.groups[layout_id] ;
32116 * http://masonry.desandro.com
32118 * The idea is to render all the bricks based on vertical width...
32120 * The original code extends 'outlayer' - we might need to use that....
32126 * @class Roo.bootstrap.LayoutMasonryAuto
32127 * @extends Roo.bootstrap.Component
32128 * Bootstrap Layout Masonry class
32131 * Create a new Element
32132 * @param {Object} config The config object
32135 Roo.bootstrap.LayoutMasonryAuto = function(config){
32136 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32139 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32142 * @cfg {Boolean} isFitWidth - resize the width..
32144 isFitWidth : false, // options..
32146 * @cfg {Boolean} isOriginLeft = left align?
32148 isOriginLeft : true,
32150 * @cfg {Boolean} isOriginTop = top align?
32152 isOriginTop : false,
32154 * @cfg {Boolean} isLayoutInstant = no animation?
32156 isLayoutInstant : false, // needed?
32158 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32160 isResizingContainer : true,
32162 * @cfg {Number} columnWidth width of the columns
32168 * @cfg {Number} maxCols maximum number of columns
32173 * @cfg {Number} padHeight padding below box..
32179 * @cfg {Boolean} isAutoInitial defalut true
32182 isAutoInitial : true,
32188 initialColumnWidth : 0,
32189 currentSize : null,
32191 colYs : null, // array.
32198 bricks: null, //CompositeElement
32199 cols : 0, // array?
32200 // element : null, // wrapped now this.el
32201 _isLayoutInited : null,
32204 getAutoCreate : function(){
32208 cls: 'blog-masonary-wrapper ' + this.cls,
32210 cls : 'mas-boxes masonary'
32217 getChildContainer: function( )
32219 if (this.boxesEl) {
32220 return this.boxesEl;
32223 this.boxesEl = this.el.select('.mas-boxes').first();
32225 return this.boxesEl;
32229 initEvents : function()
32233 if(this.isAutoInitial){
32234 Roo.log('hook children rendered');
32235 this.on('childrenrendered', function() {
32236 Roo.log('children rendered');
32243 initial : function()
32245 this.reloadItems();
32247 this.currentSize = this.el.getBox(true);
32249 /// was window resize... - let's see if this works..
32250 Roo.EventManager.onWindowResize(this.resize, this);
32252 if(!this.isAutoInitial){
32257 this.layout.defer(500,this);
32260 reloadItems: function()
32262 this.bricks = this.el.select('.masonry-brick', true);
32264 this.bricks.each(function(b) {
32265 //Roo.log(b.getSize());
32266 if (!b.attr('originalwidth')) {
32267 b.attr('originalwidth', b.getSize().width);
32272 Roo.log(this.bricks.elements.length);
32275 resize : function()
32278 var cs = this.el.getBox(true);
32280 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32281 Roo.log("no change in with or X");
32284 this.currentSize = cs;
32288 layout : function()
32291 this._resetLayout();
32292 //this._manageStamps();
32294 // don't animate first layout
32295 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32296 this.layoutItems( isInstant );
32298 // flag for initalized
32299 this._isLayoutInited = true;
32302 layoutItems : function( isInstant )
32304 //var items = this._getItemsForLayout( this.items );
32305 // original code supports filtering layout items.. we just ignore it..
32307 this._layoutItems( this.bricks , isInstant );
32309 this._postLayout();
32311 _layoutItems : function ( items , isInstant)
32313 //this.fireEvent( 'layout', this, items );
32316 if ( !items || !items.elements.length ) {
32317 // no items, emit event with empty array
32322 items.each(function(item) {
32323 Roo.log("layout item");
32325 // get x/y object from method
32326 var position = this._getItemLayoutPosition( item );
32328 position.item = item;
32329 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32330 queue.push( position );
32333 this._processLayoutQueue( queue );
32335 /** Sets position of item in DOM
32336 * @param {Element} item
32337 * @param {Number} x - horizontal position
32338 * @param {Number} y - vertical position
32339 * @param {Boolean} isInstant - disables transitions
32341 _processLayoutQueue : function( queue )
32343 for ( var i=0, len = queue.length; i < len; i++ ) {
32344 var obj = queue[i];
32345 obj.item.position('absolute');
32346 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32352 * Any logic you want to do after each layout,
32353 * i.e. size the container
32355 _postLayout : function()
32357 this.resizeContainer();
32360 resizeContainer : function()
32362 if ( !this.isResizingContainer ) {
32365 var size = this._getContainerSize();
32367 this.el.setSize(size.width,size.height);
32368 this.boxesEl.setSize(size.width,size.height);
32374 _resetLayout : function()
32376 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32377 this.colWidth = this.el.getWidth();
32378 //this.gutter = this.el.getWidth();
32380 this.measureColumns();
32386 this.colYs.push( 0 );
32392 measureColumns : function()
32394 this.getContainerWidth();
32395 // if columnWidth is 0, default to outerWidth of first item
32396 if ( !this.columnWidth ) {
32397 var firstItem = this.bricks.first();
32398 Roo.log(firstItem);
32399 this.columnWidth = this.containerWidth;
32400 if (firstItem && firstItem.attr('originalwidth') ) {
32401 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32403 // columnWidth fall back to item of first element
32404 Roo.log("set column width?");
32405 this.initialColumnWidth = this.columnWidth ;
32407 // if first elem has no width, default to size of container
32412 if (this.initialColumnWidth) {
32413 this.columnWidth = this.initialColumnWidth;
32418 // column width is fixed at the top - however if container width get's smaller we should
32421 // this bit calcs how man columns..
32423 var columnWidth = this.columnWidth += this.gutter;
32425 // calculate columns
32426 var containerWidth = this.containerWidth + this.gutter;
32428 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32429 // fix rounding errors, typically with gutters
32430 var excess = columnWidth - containerWidth % columnWidth;
32433 // if overshoot is less than a pixel, round up, otherwise floor it
32434 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32435 cols = Math[ mathMethod ]( cols );
32436 this.cols = Math.max( cols, 1 );
32437 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32439 // padding positioning..
32440 var totalColWidth = this.cols * this.columnWidth;
32441 var padavail = this.containerWidth - totalColWidth;
32442 // so for 2 columns - we need 3 'pads'
32444 var padNeeded = (1+this.cols) * this.padWidth;
32446 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32448 this.columnWidth += padExtra
32449 //this.padWidth = Math.floor(padavail / ( this.cols));
32451 // adjust colum width so that padding is fixed??
32453 // we have 3 columns ... total = width * 3
32454 // we have X left over... that should be used by
32456 //if (this.expandC) {
32464 getContainerWidth : function()
32466 /* // container is parent if fit width
32467 var container = this.isFitWidth ? this.element.parentNode : this.element;
32468 // check that this.size and size are there
32469 // IE8 triggers resize on body size change, so they might not be
32471 var size = getSize( container ); //FIXME
32472 this.containerWidth = size && size.innerWidth; //FIXME
32475 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32479 _getItemLayoutPosition : function( item ) // what is item?
32481 // we resize the item to our columnWidth..
32483 item.setWidth(this.columnWidth);
32484 item.autoBoxAdjust = false;
32486 var sz = item.getSize();
32488 // how many columns does this brick span
32489 var remainder = this.containerWidth % this.columnWidth;
32491 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32492 // round if off by 1 pixel, otherwise use ceil
32493 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32494 colSpan = Math.min( colSpan, this.cols );
32496 // normally this should be '1' as we dont' currently allow multi width columns..
32498 var colGroup = this._getColGroup( colSpan );
32499 // get the minimum Y value from the columns
32500 var minimumY = Math.min.apply( Math, colGroup );
32501 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32503 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32505 // position the brick
32507 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32508 y: this.currentSize.y + minimumY + this.padHeight
32512 // apply setHeight to necessary columns
32513 var setHeight = minimumY + sz.height + this.padHeight;
32514 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32516 var setSpan = this.cols + 1 - colGroup.length;
32517 for ( var i = 0; i < setSpan; i++ ) {
32518 this.colYs[ shortColIndex + i ] = setHeight ;
32525 * @param {Number} colSpan - number of columns the element spans
32526 * @returns {Array} colGroup
32528 _getColGroup : function( colSpan )
32530 if ( colSpan < 2 ) {
32531 // if brick spans only one column, use all the column Ys
32536 // how many different places could this brick fit horizontally
32537 var groupCount = this.cols + 1 - colSpan;
32538 // for each group potential horizontal position
32539 for ( var i = 0; i < groupCount; i++ ) {
32540 // make an array of colY values for that one group
32541 var groupColYs = this.colYs.slice( i, i + colSpan );
32542 // and get the max value of the array
32543 colGroup[i] = Math.max.apply( Math, groupColYs );
32548 _manageStamp : function( stamp )
32550 var stampSize = stamp.getSize();
32551 var offset = stamp.getBox();
32552 // get the columns that this stamp affects
32553 var firstX = this.isOriginLeft ? offset.x : offset.right;
32554 var lastX = firstX + stampSize.width;
32555 var firstCol = Math.floor( firstX / this.columnWidth );
32556 firstCol = Math.max( 0, firstCol );
32558 var lastCol = Math.floor( lastX / this.columnWidth );
32559 // lastCol should not go over if multiple of columnWidth #425
32560 lastCol -= lastX % this.columnWidth ? 0 : 1;
32561 lastCol = Math.min( this.cols - 1, lastCol );
32563 // set colYs to bottom of the stamp
32564 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32567 for ( var i = firstCol; i <= lastCol; i++ ) {
32568 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32573 _getContainerSize : function()
32575 this.maxY = Math.max.apply( Math, this.colYs );
32580 if ( this.isFitWidth ) {
32581 size.width = this._getContainerFitWidth();
32587 _getContainerFitWidth : function()
32589 var unusedCols = 0;
32590 // count unused columns
32593 if ( this.colYs[i] !== 0 ) {
32598 // fit container to columns that have been used
32599 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32602 needsResizeLayout : function()
32604 var previousWidth = this.containerWidth;
32605 this.getContainerWidth();
32606 return previousWidth !== this.containerWidth;
32621 * @class Roo.bootstrap.MasonryBrick
32622 * @extends Roo.bootstrap.Component
32623 * Bootstrap MasonryBrick class
32626 * Create a new MasonryBrick
32627 * @param {Object} config The config object
32630 Roo.bootstrap.MasonryBrick = function(config){
32632 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32634 Roo.bootstrap.MasonryBrick.register(this);
32640 * When a MasonryBrick is clcik
32641 * @param {Roo.bootstrap.MasonryBrick} this
32642 * @param {Roo.EventObject} e
32648 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32651 * @cfg {String} title
32655 * @cfg {String} html
32659 * @cfg {String} bgimage
32663 * @cfg {String} videourl
32667 * @cfg {String} cls
32671 * @cfg {String} href
32675 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32680 * @cfg {String} placetitle (center|bottom)
32685 * @cfg {Boolean} isFitContainer defalut true
32687 isFitContainer : true,
32690 * @cfg {Boolean} preventDefault defalut false
32692 preventDefault : false,
32695 * @cfg {Boolean} inverse defalut false
32697 maskInverse : false,
32699 getAutoCreate : function()
32701 if(!this.isFitContainer){
32702 return this.getSplitAutoCreate();
32705 var cls = 'masonry-brick masonry-brick-full';
32707 if(this.href.length){
32708 cls += ' masonry-brick-link';
32711 if(this.bgimage.length){
32712 cls += ' masonry-brick-image';
32715 if(this.maskInverse){
32716 cls += ' mask-inverse';
32719 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32720 cls += ' enable-mask';
32724 cls += ' masonry-' + this.size + '-brick';
32727 if(this.placetitle.length){
32729 switch (this.placetitle) {
32731 cls += ' masonry-center-title';
32734 cls += ' masonry-bottom-title';
32741 if(!this.html.length && !this.bgimage.length){
32742 cls += ' masonry-center-title';
32745 if(!this.html.length && this.bgimage.length){
32746 cls += ' masonry-bottom-title';
32751 cls += ' ' + this.cls;
32755 tag: (this.href.length) ? 'a' : 'div',
32760 cls: 'masonry-brick-mask'
32764 cls: 'masonry-brick-paragraph',
32770 if(this.href.length){
32771 cfg.href = this.href;
32774 var cn = cfg.cn[1].cn;
32776 if(this.title.length){
32779 cls: 'masonry-brick-title',
32784 if(this.html.length){
32787 cls: 'masonry-brick-text',
32792 if (!this.title.length && !this.html.length) {
32793 cfg.cn[1].cls += ' hide';
32796 if(this.bgimage.length){
32799 cls: 'masonry-brick-image-view',
32804 if(this.videourl.length){
32805 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32806 // youtube support only?
32809 cls: 'masonry-brick-image-view',
32812 allowfullscreen : true
32820 getSplitAutoCreate : function()
32822 var cls = 'masonry-brick masonry-brick-split';
32824 if(this.href.length){
32825 cls += ' masonry-brick-link';
32828 if(this.bgimage.length){
32829 cls += ' masonry-brick-image';
32833 cls += ' masonry-' + this.size + '-brick';
32836 switch (this.placetitle) {
32838 cls += ' masonry-center-title';
32841 cls += ' masonry-bottom-title';
32844 if(!this.bgimage.length){
32845 cls += ' masonry-center-title';
32848 if(this.bgimage.length){
32849 cls += ' masonry-bottom-title';
32855 cls += ' ' + this.cls;
32859 tag: (this.href.length) ? 'a' : 'div',
32864 cls: 'masonry-brick-split-head',
32868 cls: 'masonry-brick-paragraph',
32875 cls: 'masonry-brick-split-body',
32881 if(this.href.length){
32882 cfg.href = this.href;
32885 if(this.title.length){
32886 cfg.cn[0].cn[0].cn.push({
32888 cls: 'masonry-brick-title',
32893 if(this.html.length){
32894 cfg.cn[1].cn.push({
32896 cls: 'masonry-brick-text',
32901 if(this.bgimage.length){
32902 cfg.cn[0].cn.push({
32904 cls: 'masonry-brick-image-view',
32909 if(this.videourl.length){
32910 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32911 // youtube support only?
32912 cfg.cn[0].cn.cn.push({
32914 cls: 'masonry-brick-image-view',
32917 allowfullscreen : true
32924 initEvents: function()
32926 switch (this.size) {
32959 this.el.on('touchstart', this.onTouchStart, this);
32960 this.el.on('touchmove', this.onTouchMove, this);
32961 this.el.on('touchend', this.onTouchEnd, this);
32962 this.el.on('contextmenu', this.onContextMenu, this);
32964 this.el.on('mouseenter' ,this.enter, this);
32965 this.el.on('mouseleave', this.leave, this);
32966 this.el.on('click', this.onClick, this);
32969 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32970 this.parent().bricks.push(this);
32975 onClick: function(e, el)
32977 var time = this.endTimer - this.startTimer;
32978 // Roo.log(e.preventDefault());
32981 e.preventDefault();
32986 if(!this.preventDefault){
32990 e.preventDefault();
32992 if (this.activeClass != '') {
32993 this.selectBrick();
32996 this.fireEvent('click', this, e);
32999 enter: function(e, el)
33001 e.preventDefault();
33003 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33007 if(this.bgimage.length && this.html.length){
33008 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33012 leave: function(e, el)
33014 e.preventDefault();
33016 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33020 if(this.bgimage.length && this.html.length){
33021 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33025 onTouchStart: function(e, el)
33027 // e.preventDefault();
33029 this.touchmoved = false;
33031 if(!this.isFitContainer){
33035 if(!this.bgimage.length || !this.html.length){
33039 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33041 this.timer = new Date().getTime();
33045 onTouchMove: function(e, el)
33047 this.touchmoved = true;
33050 onContextMenu : function(e,el)
33052 e.preventDefault();
33053 e.stopPropagation();
33057 onTouchEnd: function(e, el)
33059 // e.preventDefault();
33061 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33068 if(!this.bgimage.length || !this.html.length){
33070 if(this.href.length){
33071 window.location.href = this.href;
33077 if(!this.isFitContainer){
33081 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33083 window.location.href = this.href;
33086 //selection on single brick only
33087 selectBrick : function() {
33089 if (!this.parentId) {
33093 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33094 var index = m.selectedBrick.indexOf(this.id);
33097 m.selectedBrick.splice(index,1);
33098 this.el.removeClass(this.activeClass);
33102 for(var i = 0; i < m.selectedBrick.length; i++) {
33103 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33104 b.el.removeClass(b.activeClass);
33107 m.selectedBrick = [];
33109 m.selectedBrick.push(this.id);
33110 this.el.addClass(this.activeClass);
33114 isSelected : function(){
33115 return this.el.hasClass(this.activeClass);
33120 Roo.apply(Roo.bootstrap.MasonryBrick, {
33123 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33125 * register a Masonry Brick
33126 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33129 register : function(brick)
33131 //this.groups[brick.id] = brick;
33132 this.groups.add(brick.id, brick);
33135 * fetch a masonry brick based on the masonry brick ID
33136 * @param {string} the masonry brick to add
33137 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33140 get: function(brick_id)
33142 // if (typeof(this.groups[brick_id]) == 'undefined') {
33145 // return this.groups[brick_id] ;
33147 if(this.groups.key(brick_id)) {
33148 return this.groups.key(brick_id);
33166 * @class Roo.bootstrap.Brick
33167 * @extends Roo.bootstrap.Component
33168 * Bootstrap Brick class
33171 * Create a new Brick
33172 * @param {Object} config The config object
33175 Roo.bootstrap.Brick = function(config){
33176 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33182 * When a Brick is click
33183 * @param {Roo.bootstrap.Brick} this
33184 * @param {Roo.EventObject} e
33190 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33193 * @cfg {String} title
33197 * @cfg {String} html
33201 * @cfg {String} bgimage
33205 * @cfg {String} cls
33209 * @cfg {String} href
33213 * @cfg {String} video
33217 * @cfg {Boolean} square
33221 getAutoCreate : function()
33223 var cls = 'roo-brick';
33225 if(this.href.length){
33226 cls += ' roo-brick-link';
33229 if(this.bgimage.length){
33230 cls += ' roo-brick-image';
33233 if(!this.html.length && !this.bgimage.length){
33234 cls += ' roo-brick-center-title';
33237 if(!this.html.length && this.bgimage.length){
33238 cls += ' roo-brick-bottom-title';
33242 cls += ' ' + this.cls;
33246 tag: (this.href.length) ? 'a' : 'div',
33251 cls: 'roo-brick-paragraph',
33257 if(this.href.length){
33258 cfg.href = this.href;
33261 var cn = cfg.cn[0].cn;
33263 if(this.title.length){
33266 cls: 'roo-brick-title',
33271 if(this.html.length){
33274 cls: 'roo-brick-text',
33281 if(this.bgimage.length){
33284 cls: 'roo-brick-image-view',
33292 initEvents: function()
33294 if(this.title.length || this.html.length){
33295 this.el.on('mouseenter' ,this.enter, this);
33296 this.el.on('mouseleave', this.leave, this);
33299 Roo.EventManager.onWindowResize(this.resize, this);
33301 if(this.bgimage.length){
33302 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33303 this.imageEl.on('load', this.onImageLoad, this);
33310 onImageLoad : function()
33315 resize : function()
33317 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33319 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33321 if(this.bgimage.length){
33322 var image = this.el.select('.roo-brick-image-view', true).first();
33324 image.setWidth(paragraph.getWidth());
33327 image.setHeight(paragraph.getWidth());
33330 this.el.setHeight(image.getHeight());
33331 paragraph.setHeight(image.getHeight());
33337 enter: function(e, el)
33339 e.preventDefault();
33341 if(this.bgimage.length){
33342 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33343 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33347 leave: function(e, el)
33349 e.preventDefault();
33351 if(this.bgimage.length){
33352 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33353 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33368 * @class Roo.bootstrap.NumberField
33369 * @extends Roo.bootstrap.Input
33370 * Bootstrap NumberField class
33376 * Create a new NumberField
33377 * @param {Object} config The config object
33380 Roo.bootstrap.NumberField = function(config){
33381 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33384 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33387 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33389 allowDecimals : true,
33391 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33393 decimalSeparator : ".",
33395 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33397 decimalPrecision : 2,
33399 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33401 allowNegative : true,
33404 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33408 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33410 minValue : Number.NEGATIVE_INFINITY,
33412 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33414 maxValue : Number.MAX_VALUE,
33416 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33418 minText : "The minimum value for this field is {0}",
33420 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33422 maxText : "The maximum value for this field is {0}",
33424 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33425 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33427 nanText : "{0} is not a valid number",
33429 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33431 thousandsDelimiter : false,
33433 * @cfg {String} valueAlign alignment of value
33435 valueAlign : "left",
33437 getAutoCreate : function()
33439 var hiddenInput = {
33443 cls: 'hidden-number-input'
33447 hiddenInput.name = this.name;
33452 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33454 this.name = hiddenInput.name;
33456 if(cfg.cn.length > 0) {
33457 cfg.cn.push(hiddenInput);
33464 initEvents : function()
33466 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33468 var allowed = "0123456789";
33470 if(this.allowDecimals){
33471 allowed += this.decimalSeparator;
33474 if(this.allowNegative){
33478 if(this.thousandsDelimiter) {
33482 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33484 var keyPress = function(e){
33486 var k = e.getKey();
33488 var c = e.getCharCode();
33491 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33492 allowed.indexOf(String.fromCharCode(c)) === -1
33498 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33502 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33507 this.el.on("keypress", keyPress, this);
33510 validateValue : function(value)
33513 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33517 var num = this.parseValue(value);
33520 this.markInvalid(String.format(this.nanText, value));
33524 if(num < this.minValue){
33525 this.markInvalid(String.format(this.minText, this.minValue));
33529 if(num > this.maxValue){
33530 this.markInvalid(String.format(this.maxText, this.maxValue));
33537 getValue : function()
33539 var v = this.hiddenEl().getValue();
33541 return this.fixPrecision(this.parseValue(v));
33544 parseValue : function(value)
33546 if(this.thousandsDelimiter) {
33548 r = new RegExp(",", "g");
33549 value = value.replace(r, "");
33552 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33553 return isNaN(value) ? '' : value;
33556 fixPrecision : function(value)
33558 if(this.thousandsDelimiter) {
33560 r = new RegExp(",", "g");
33561 value = value.replace(r, "");
33564 var nan = isNaN(value);
33566 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33567 return nan ? '' : value;
33569 return parseFloat(value).toFixed(this.decimalPrecision);
33572 setValue : function(v)
33574 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33580 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33582 this.inputEl().dom.value = (v == '') ? '' :
33583 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33585 if(!this.allowZero && v === '0') {
33586 this.hiddenEl().dom.value = '';
33587 this.inputEl().dom.value = '';
33594 decimalPrecisionFcn : function(v)
33596 return Math.floor(v);
33599 beforeBlur : function()
33601 var v = this.parseValue(this.getRawValue());
33603 if(v || v === 0 || v === ''){
33608 hiddenEl : function()
33610 return this.el.select('input.hidden-number-input',true).first();
33622 * @class Roo.bootstrap.DocumentSlider
33623 * @extends Roo.bootstrap.Component
33624 * Bootstrap DocumentSlider class
33627 * Create a new DocumentViewer
33628 * @param {Object} config The config object
33631 Roo.bootstrap.DocumentSlider = function(config){
33632 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33639 * Fire after initEvent
33640 * @param {Roo.bootstrap.DocumentSlider} this
33645 * Fire after update
33646 * @param {Roo.bootstrap.DocumentSlider} this
33652 * @param {Roo.bootstrap.DocumentSlider} this
33658 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33664 getAutoCreate : function()
33668 cls : 'roo-document-slider',
33672 cls : 'roo-document-slider-header',
33676 cls : 'roo-document-slider-header-title'
33682 cls : 'roo-document-slider-body',
33686 cls : 'roo-document-slider-prev',
33690 cls : 'fa fa-chevron-left'
33696 cls : 'roo-document-slider-thumb',
33700 cls : 'roo-document-slider-image'
33706 cls : 'roo-document-slider-next',
33710 cls : 'fa fa-chevron-right'
33722 initEvents : function()
33724 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33725 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33727 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33728 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33730 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33731 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33733 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33734 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33736 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33737 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33739 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33740 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33742 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33743 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33745 this.thumbEl.on('click', this.onClick, this);
33747 this.prevIndicator.on('click', this.prev, this);
33749 this.nextIndicator.on('click', this.next, this);
33753 initial : function()
33755 if(this.files.length){
33756 this.indicator = 1;
33760 this.fireEvent('initial', this);
33763 update : function()
33765 this.imageEl.attr('src', this.files[this.indicator - 1]);
33767 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33769 this.prevIndicator.show();
33771 if(this.indicator == 1){
33772 this.prevIndicator.hide();
33775 this.nextIndicator.show();
33777 if(this.indicator == this.files.length){
33778 this.nextIndicator.hide();
33781 this.thumbEl.scrollTo('top');
33783 this.fireEvent('update', this);
33786 onClick : function(e)
33788 e.preventDefault();
33790 this.fireEvent('click', this);
33795 e.preventDefault();
33797 this.indicator = Math.max(1, this.indicator - 1);
33804 e.preventDefault();
33806 this.indicator = Math.min(this.files.length, this.indicator + 1);
33820 * @class Roo.bootstrap.RadioSet
33821 * @extends Roo.bootstrap.Input
33822 * Bootstrap RadioSet class
33823 * @cfg {String} indicatorpos (left|right) default left
33824 * @cfg {Boolean} inline (true|false) inline the element (default true)
33825 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33827 * Create a new RadioSet
33828 * @param {Object} config The config object
33831 Roo.bootstrap.RadioSet = function(config){
33833 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33837 Roo.bootstrap.RadioSet.register(this);
33842 * Fires when the element is checked or unchecked.
33843 * @param {Roo.bootstrap.RadioSet} this This radio
33844 * @param {Roo.bootstrap.Radio} item The checked item
33849 * Fires when the element is click.
33850 * @param {Roo.bootstrap.RadioSet} this This radio set
33851 * @param {Roo.bootstrap.Radio} item The checked item
33852 * @param {Roo.EventObject} e The event object
33859 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33867 indicatorpos : 'left',
33869 getAutoCreate : function()
33873 cls : 'roo-radio-set-label',
33877 html : this.fieldLabel
33882 if(this.indicatorpos == 'left'){
33885 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33886 tooltip : 'This field is required'
33891 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33892 tooltip : 'This field is required'
33898 cls : 'roo-radio-set-items'
33901 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33903 if (align === 'left' && this.fieldLabel.length) {
33906 cls : "roo-radio-set-right",
33912 if(this.labelWidth > 12){
33913 label.style = "width: " + this.labelWidth + 'px';
33916 if(this.labelWidth < 13 && this.labelmd == 0){
33917 this.labelmd = this.labelWidth;
33920 if(this.labellg > 0){
33921 label.cls += ' col-lg-' + this.labellg;
33922 items.cls += ' col-lg-' + (12 - this.labellg);
33925 if(this.labelmd > 0){
33926 label.cls += ' col-md-' + this.labelmd;
33927 items.cls += ' col-md-' + (12 - this.labelmd);
33930 if(this.labelsm > 0){
33931 label.cls += ' col-sm-' + this.labelsm;
33932 items.cls += ' col-sm-' + (12 - this.labelsm);
33935 if(this.labelxs > 0){
33936 label.cls += ' col-xs-' + this.labelxs;
33937 items.cls += ' col-xs-' + (12 - this.labelxs);
33943 cls : 'roo-radio-set',
33947 cls : 'roo-radio-set-input',
33950 value : this.value ? this.value : ''
33957 if(this.weight.length){
33958 cfg.cls += ' roo-radio-' + this.weight;
33962 cfg.cls += ' roo-radio-set-inline';
33966 ['xs','sm','md','lg'].map(function(size){
33967 if (settings[size]) {
33968 cfg.cls += ' col-' + size + '-' + settings[size];
33976 initEvents : function()
33978 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33979 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33981 if(!this.fieldLabel.length){
33982 this.labelEl.hide();
33985 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33986 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33988 this.indicator = this.indicatorEl();
33990 if(this.indicator){
33991 this.indicator.addClass('invisible');
33994 this.originalValue = this.getValue();
33998 inputEl: function ()
34000 return this.el.select('.roo-radio-set-input', true).first();
34003 getChildContainer : function()
34005 return this.itemsEl;
34008 register : function(item)
34010 this.radioes.push(item);
34014 validate : function()
34016 if(this.getVisibilityEl().hasClass('hidden')){
34022 Roo.each(this.radioes, function(i){
34031 if(this.allowBlank) {
34035 if(this.disabled || valid){
34040 this.markInvalid();
34045 markValid : function()
34047 if(this.labelEl.isVisible(true)){
34048 this.indicatorEl().removeClass('visible');
34049 this.indicatorEl().addClass('invisible');
34052 this.el.removeClass([this.invalidClass, this.validClass]);
34053 this.el.addClass(this.validClass);
34055 this.fireEvent('valid', this);
34058 markInvalid : function(msg)
34060 if(this.allowBlank || this.disabled){
34064 if(this.labelEl.isVisible(true)){
34065 this.indicatorEl().removeClass('invisible');
34066 this.indicatorEl().addClass('visible');
34069 this.el.removeClass([this.invalidClass, this.validClass]);
34070 this.el.addClass(this.invalidClass);
34072 this.fireEvent('invalid', this, msg);
34076 setValue : function(v, suppressEvent)
34078 if(this.value === v){
34085 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34088 Roo.each(this.radioes, function(i){
34090 i.el.removeClass('checked');
34093 Roo.each(this.radioes, function(i){
34095 if(i.value === v || i.value.toString() === v.toString()){
34097 i.el.addClass('checked');
34099 if(suppressEvent !== true){
34100 this.fireEvent('check', this, i);
34111 clearInvalid : function(){
34113 if(!this.el || this.preventMark){
34117 this.el.removeClass([this.invalidClass]);
34119 this.fireEvent('valid', this);
34124 Roo.apply(Roo.bootstrap.RadioSet, {
34128 register : function(set)
34130 this.groups[set.name] = set;
34133 get: function(name)
34135 if (typeof(this.groups[name]) == 'undefined') {
34139 return this.groups[name] ;
34145 * Ext JS Library 1.1.1
34146 * Copyright(c) 2006-2007, Ext JS, LLC.
34148 * Originally Released Under LGPL - original licence link has changed is not relivant.
34151 * <script type="text/javascript">
34156 * @class Roo.bootstrap.SplitBar
34157 * @extends Roo.util.Observable
34158 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34162 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34163 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34164 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34165 split.minSize = 100;
34166 split.maxSize = 600;
34167 split.animate = true;
34168 split.on('moved', splitterMoved);
34171 * Create a new SplitBar
34172 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34173 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34174 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34175 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34176 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34177 position of the SplitBar).
34179 Roo.bootstrap.SplitBar = function(cfg){
34184 // dragElement : elm
34185 // resizingElement: el,
34187 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34188 // placement : Roo.bootstrap.SplitBar.LEFT ,
34189 // existingProxy ???
34192 this.el = Roo.get(cfg.dragElement, true);
34193 this.el.dom.unselectable = "on";
34195 this.resizingEl = Roo.get(cfg.resizingElement, true);
34199 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34200 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34203 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34206 * The minimum size of the resizing element. (Defaults to 0)
34212 * The maximum size of the resizing element. (Defaults to 2000)
34215 this.maxSize = 2000;
34218 * Whether to animate the transition to the new size
34221 this.animate = false;
34224 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34227 this.useShim = false;
34232 if(!cfg.existingProxy){
34234 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34236 this.proxy = Roo.get(cfg.existingProxy).dom;
34239 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34242 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34245 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34248 this.dragSpecs = {};
34251 * @private The adapter to use to positon and resize elements
34253 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34254 this.adapter.init(this);
34256 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34258 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34259 this.el.addClass("roo-splitbar-h");
34262 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34263 this.el.addClass("roo-splitbar-v");
34269 * Fires when the splitter is moved (alias for {@link #event-moved})
34270 * @param {Roo.bootstrap.SplitBar} this
34271 * @param {Number} newSize the new width or height
34276 * Fires when the splitter is moved
34277 * @param {Roo.bootstrap.SplitBar} this
34278 * @param {Number} newSize the new width or height
34282 * @event beforeresize
34283 * Fires before the splitter is dragged
34284 * @param {Roo.bootstrap.SplitBar} this
34286 "beforeresize" : true,
34288 "beforeapply" : true
34291 Roo.util.Observable.call(this);
34294 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34295 onStartProxyDrag : function(x, y){
34296 this.fireEvent("beforeresize", this);
34298 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34300 o.enableDisplayMode("block");
34301 // all splitbars share the same overlay
34302 Roo.bootstrap.SplitBar.prototype.overlay = o;
34304 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34305 this.overlay.show();
34306 Roo.get(this.proxy).setDisplayed("block");
34307 var size = this.adapter.getElementSize(this);
34308 this.activeMinSize = this.getMinimumSize();;
34309 this.activeMaxSize = this.getMaximumSize();;
34310 var c1 = size - this.activeMinSize;
34311 var c2 = Math.max(this.activeMaxSize - size, 0);
34312 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34313 this.dd.resetConstraints();
34314 this.dd.setXConstraint(
34315 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34316 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34318 this.dd.setYConstraint(0, 0);
34320 this.dd.resetConstraints();
34321 this.dd.setXConstraint(0, 0);
34322 this.dd.setYConstraint(
34323 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34324 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34327 this.dragSpecs.startSize = size;
34328 this.dragSpecs.startPoint = [x, y];
34329 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34333 * @private Called after the drag operation by the DDProxy
34335 onEndProxyDrag : function(e){
34336 Roo.get(this.proxy).setDisplayed(false);
34337 var endPoint = Roo.lib.Event.getXY(e);
34339 this.overlay.hide();
34342 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34343 newSize = this.dragSpecs.startSize +
34344 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34345 endPoint[0] - this.dragSpecs.startPoint[0] :
34346 this.dragSpecs.startPoint[0] - endPoint[0]
34349 newSize = this.dragSpecs.startSize +
34350 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34351 endPoint[1] - this.dragSpecs.startPoint[1] :
34352 this.dragSpecs.startPoint[1] - endPoint[1]
34355 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34356 if(newSize != this.dragSpecs.startSize){
34357 if(this.fireEvent('beforeapply', this, newSize) !== false){
34358 this.adapter.setElementSize(this, newSize);
34359 this.fireEvent("moved", this, newSize);
34360 this.fireEvent("resize", this, newSize);
34366 * Get the adapter this SplitBar uses
34367 * @return The adapter object
34369 getAdapter : function(){
34370 return this.adapter;
34374 * Set the adapter this SplitBar uses
34375 * @param {Object} adapter A SplitBar adapter object
34377 setAdapter : function(adapter){
34378 this.adapter = adapter;
34379 this.adapter.init(this);
34383 * Gets the minimum size for the resizing element
34384 * @return {Number} The minimum size
34386 getMinimumSize : function(){
34387 return this.minSize;
34391 * Sets the minimum size for the resizing element
34392 * @param {Number} minSize The minimum size
34394 setMinimumSize : function(minSize){
34395 this.minSize = minSize;
34399 * Gets the maximum size for the resizing element
34400 * @return {Number} The maximum size
34402 getMaximumSize : function(){
34403 return this.maxSize;
34407 * Sets the maximum size for the resizing element
34408 * @param {Number} maxSize The maximum size
34410 setMaximumSize : function(maxSize){
34411 this.maxSize = maxSize;
34415 * Sets the initialize size for the resizing element
34416 * @param {Number} size The initial size
34418 setCurrentSize : function(size){
34419 var oldAnimate = this.animate;
34420 this.animate = false;
34421 this.adapter.setElementSize(this, size);
34422 this.animate = oldAnimate;
34426 * Destroy this splitbar.
34427 * @param {Boolean} removeEl True to remove the element
34429 destroy : function(removeEl){
34431 this.shim.remove();
34434 this.proxy.parentNode.removeChild(this.proxy);
34442 * @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.
34444 Roo.bootstrap.SplitBar.createProxy = function(dir){
34445 var proxy = new Roo.Element(document.createElement("div"));
34446 proxy.unselectable();
34447 var cls = 'roo-splitbar-proxy';
34448 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34449 document.body.appendChild(proxy.dom);
34454 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34455 * Default Adapter. It assumes the splitter and resizing element are not positioned
34456 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34458 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34461 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34462 // do nothing for now
34463 init : function(s){
34467 * Called before drag operations to get the current size of the resizing element.
34468 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34470 getElementSize : function(s){
34471 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34472 return s.resizingEl.getWidth();
34474 return s.resizingEl.getHeight();
34479 * Called after drag operations to set the size of the resizing element.
34480 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34481 * @param {Number} newSize The new size to set
34482 * @param {Function} onComplete A function to be invoked when resizing is complete
34484 setElementSize : function(s, newSize, onComplete){
34485 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34487 s.resizingEl.setWidth(newSize);
34489 onComplete(s, newSize);
34492 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34497 s.resizingEl.setHeight(newSize);
34499 onComplete(s, newSize);
34502 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34509 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34510 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34511 * Adapter that moves the splitter element to align with the resized sizing element.
34512 * Used with an absolute positioned SplitBar.
34513 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34514 * document.body, make sure you assign an id to the body element.
34516 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34517 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34518 this.container = Roo.get(container);
34521 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34522 init : function(s){
34523 this.basic.init(s);
34526 getElementSize : function(s){
34527 return this.basic.getElementSize(s);
34530 setElementSize : function(s, newSize, onComplete){
34531 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34534 moveSplitter : function(s){
34535 var yes = Roo.bootstrap.SplitBar;
34536 switch(s.placement){
34538 s.el.setX(s.resizingEl.getRight());
34541 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34544 s.el.setY(s.resizingEl.getBottom());
34547 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34554 * Orientation constant - Create a vertical SplitBar
34558 Roo.bootstrap.SplitBar.VERTICAL = 1;
34561 * Orientation constant - Create a horizontal SplitBar
34565 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34568 * Placement constant - The resizing element is to the left of the splitter element
34572 Roo.bootstrap.SplitBar.LEFT = 1;
34575 * Placement constant - The resizing element is to the right of the splitter element
34579 Roo.bootstrap.SplitBar.RIGHT = 2;
34582 * Placement constant - The resizing element is positioned above the splitter element
34586 Roo.bootstrap.SplitBar.TOP = 3;
34589 * Placement constant - The resizing element is positioned under splitter element
34593 Roo.bootstrap.SplitBar.BOTTOM = 4;
34594 Roo.namespace("Roo.bootstrap.layout");/*
34596 * Ext JS Library 1.1.1
34597 * Copyright(c) 2006-2007, Ext JS, LLC.
34599 * Originally Released Under LGPL - original licence link has changed is not relivant.
34602 * <script type="text/javascript">
34606 * @class Roo.bootstrap.layout.Manager
34607 * @extends Roo.bootstrap.Component
34608 * Base class for layout managers.
34610 Roo.bootstrap.layout.Manager = function(config)
34612 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34618 /** false to disable window resize monitoring @type Boolean */
34619 this.monitorWindowResize = true;
34624 * Fires when a layout is performed.
34625 * @param {Roo.LayoutManager} this
34629 * @event regionresized
34630 * Fires when the user resizes a region.
34631 * @param {Roo.LayoutRegion} region The resized region
34632 * @param {Number} newSize The new size (width for east/west, height for north/south)
34634 "regionresized" : true,
34636 * @event regioncollapsed
34637 * Fires when a region is collapsed.
34638 * @param {Roo.LayoutRegion} region The collapsed region
34640 "regioncollapsed" : true,
34642 * @event regionexpanded
34643 * Fires when a region is expanded.
34644 * @param {Roo.LayoutRegion} region The expanded region
34646 "regionexpanded" : true
34648 this.updating = false;
34651 this.el = Roo.get(config.el);
34657 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34662 monitorWindowResize : true,
34668 onRender : function(ct, position)
34671 this.el = Roo.get(ct);
34674 //this.fireEvent('render',this);
34678 initEvents: function()
34682 // ie scrollbar fix
34683 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34684 document.body.scroll = "no";
34685 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34686 this.el.position('relative');
34688 this.id = this.el.id;
34689 this.el.addClass("roo-layout-container");
34690 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34691 if(this.el.dom != document.body ) {
34692 this.el.on('resize', this.layout,this);
34693 this.el.on('show', this.layout,this);
34699 * Returns true if this layout is currently being updated
34700 * @return {Boolean}
34702 isUpdating : function(){
34703 return this.updating;
34707 * Suspend the LayoutManager from doing auto-layouts while
34708 * making multiple add or remove calls
34710 beginUpdate : function(){
34711 this.updating = true;
34715 * Restore auto-layouts and optionally disable the manager from performing a layout
34716 * @param {Boolean} noLayout true to disable a layout update
34718 endUpdate : function(noLayout){
34719 this.updating = false;
34725 layout: function(){
34729 onRegionResized : function(region, newSize){
34730 this.fireEvent("regionresized", region, newSize);
34734 onRegionCollapsed : function(region){
34735 this.fireEvent("regioncollapsed", region);
34738 onRegionExpanded : function(region){
34739 this.fireEvent("regionexpanded", region);
34743 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34744 * performs box-model adjustments.
34745 * @return {Object} The size as an object {width: (the width), height: (the height)}
34747 getViewSize : function()
34750 if(this.el.dom != document.body){
34751 size = this.el.getSize();
34753 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34755 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34756 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34761 * Returns the Element this layout is bound to.
34762 * @return {Roo.Element}
34764 getEl : function(){
34769 * Returns the specified region.
34770 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34771 * @return {Roo.LayoutRegion}
34773 getRegion : function(target){
34774 return this.regions[target.toLowerCase()];
34777 onWindowResize : function(){
34778 if(this.monitorWindowResize){
34785 * Ext JS Library 1.1.1
34786 * Copyright(c) 2006-2007, Ext JS, LLC.
34788 * Originally Released Under LGPL - original licence link has changed is not relivant.
34791 * <script type="text/javascript">
34794 * @class Roo.bootstrap.layout.Border
34795 * @extends Roo.bootstrap.layout.Manager
34796 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34797 * please see: examples/bootstrap/nested.html<br><br>
34799 <b>The container the layout is rendered into can be either the body element or any other element.
34800 If it is not the body element, the container needs to either be an absolute positioned element,
34801 or you will need to add "position:relative" to the css of the container. You will also need to specify
34802 the container size if it is not the body element.</b>
34805 * Create a new Border
34806 * @param {Object} config Configuration options
34808 Roo.bootstrap.layout.Border = function(config){
34809 config = config || {};
34810 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34814 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34815 if(config[region]){
34816 config[region].region = region;
34817 this.addRegion(config[region]);
34823 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34825 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34827 * Creates and adds a new region if it doesn't already exist.
34828 * @param {String} target The target region key (north, south, east, west or center).
34829 * @param {Object} config The regions config object
34830 * @return {BorderLayoutRegion} The new region
34832 addRegion : function(config)
34834 if(!this.regions[config.region]){
34835 var r = this.factory(config);
34836 this.bindRegion(r);
34838 return this.regions[config.region];
34842 bindRegion : function(r){
34843 this.regions[r.config.region] = r;
34845 r.on("visibilitychange", this.layout, this);
34846 r.on("paneladded", this.layout, this);
34847 r.on("panelremoved", this.layout, this);
34848 r.on("invalidated", this.layout, this);
34849 r.on("resized", this.onRegionResized, this);
34850 r.on("collapsed", this.onRegionCollapsed, this);
34851 r.on("expanded", this.onRegionExpanded, this);
34855 * Performs a layout update.
34857 layout : function()
34859 if(this.updating) {
34863 // render all the rebions if they have not been done alreayd?
34864 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34865 if(this.regions[region] && !this.regions[region].bodyEl){
34866 this.regions[region].onRender(this.el)
34870 var size = this.getViewSize();
34871 var w = size.width;
34872 var h = size.height;
34877 //var x = 0, y = 0;
34879 var rs = this.regions;
34880 var north = rs["north"];
34881 var south = rs["south"];
34882 var west = rs["west"];
34883 var east = rs["east"];
34884 var center = rs["center"];
34885 //if(this.hideOnLayout){ // not supported anymore
34886 //c.el.setStyle("display", "none");
34888 if(north && north.isVisible()){
34889 var b = north.getBox();
34890 var m = north.getMargins();
34891 b.width = w - (m.left+m.right);
34894 centerY = b.height + b.y + m.bottom;
34895 centerH -= centerY;
34896 north.updateBox(this.safeBox(b));
34898 if(south && south.isVisible()){
34899 var b = south.getBox();
34900 var m = south.getMargins();
34901 b.width = w - (m.left+m.right);
34903 var totalHeight = (b.height + m.top + m.bottom);
34904 b.y = h - totalHeight + m.top;
34905 centerH -= totalHeight;
34906 south.updateBox(this.safeBox(b));
34908 if(west && west.isVisible()){
34909 var b = west.getBox();
34910 var m = west.getMargins();
34911 b.height = centerH - (m.top+m.bottom);
34913 b.y = centerY + m.top;
34914 var totalWidth = (b.width + m.left + m.right);
34915 centerX += totalWidth;
34916 centerW -= totalWidth;
34917 west.updateBox(this.safeBox(b));
34919 if(east && east.isVisible()){
34920 var b = east.getBox();
34921 var m = east.getMargins();
34922 b.height = centerH - (m.top+m.bottom);
34923 var totalWidth = (b.width + m.left + m.right);
34924 b.x = w - totalWidth + m.left;
34925 b.y = centerY + m.top;
34926 centerW -= totalWidth;
34927 east.updateBox(this.safeBox(b));
34930 var m = center.getMargins();
34932 x: centerX + m.left,
34933 y: centerY + m.top,
34934 width: centerW - (m.left+m.right),
34935 height: centerH - (m.top+m.bottom)
34937 //if(this.hideOnLayout){
34938 //center.el.setStyle("display", "block");
34940 center.updateBox(this.safeBox(centerBox));
34943 this.fireEvent("layout", this);
34947 safeBox : function(box){
34948 box.width = Math.max(0, box.width);
34949 box.height = Math.max(0, box.height);
34954 * Adds a ContentPanel (or subclass) to this layout.
34955 * @param {String} target The target region key (north, south, east, west or center).
34956 * @param {Roo.ContentPanel} panel The panel to add
34957 * @return {Roo.ContentPanel} The added panel
34959 add : function(target, panel){
34961 target = target.toLowerCase();
34962 return this.regions[target].add(panel);
34966 * Remove a ContentPanel (or subclass) to this layout.
34967 * @param {String} target The target region key (north, south, east, west or center).
34968 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34969 * @return {Roo.ContentPanel} The removed panel
34971 remove : function(target, panel){
34972 target = target.toLowerCase();
34973 return this.regions[target].remove(panel);
34977 * Searches all regions for a panel with the specified id
34978 * @param {String} panelId
34979 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34981 findPanel : function(panelId){
34982 var rs = this.regions;
34983 for(var target in rs){
34984 if(typeof rs[target] != "function"){
34985 var p = rs[target].getPanel(panelId);
34995 * Searches all regions for a panel with the specified id and activates (shows) it.
34996 * @param {String/ContentPanel} panelId The panels id or the panel itself
34997 * @return {Roo.ContentPanel} The shown panel or null
34999 showPanel : function(panelId) {
35000 var rs = this.regions;
35001 for(var target in rs){
35002 var r = rs[target];
35003 if(typeof r != "function"){
35004 if(r.hasPanel(panelId)){
35005 return r.showPanel(panelId);
35013 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35014 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35017 restoreState : function(provider){
35019 provider = Roo.state.Manager;
35021 var sm = new Roo.LayoutStateManager();
35022 sm.init(this, provider);
35028 * Adds a xtype elements to the layout.
35032 xtype : 'ContentPanel',
35039 xtype : 'NestedLayoutPanel',
35045 items : [ ... list of content panels or nested layout panels.. ]
35049 * @param {Object} cfg Xtype definition of item to add.
35051 addxtype : function(cfg)
35053 // basically accepts a pannel...
35054 // can accept a layout region..!?!?
35055 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35058 // theory? children can only be panels??
35060 //if (!cfg.xtype.match(/Panel$/)) {
35065 if (typeof(cfg.region) == 'undefined') {
35066 Roo.log("Failed to add Panel, region was not set");
35070 var region = cfg.region;
35076 xitems = cfg.items;
35083 case 'Content': // ContentPanel (el, cfg)
35084 case 'Scroll': // ContentPanel (el, cfg)
35086 cfg.autoCreate = true;
35087 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35089 // var el = this.el.createChild();
35090 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35093 this.add(region, ret);
35097 case 'TreePanel': // our new panel!
35098 cfg.el = this.el.createChild();
35099 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35100 this.add(region, ret);
35105 // create a new Layout (which is a Border Layout...
35107 var clayout = cfg.layout;
35108 clayout.el = this.el.createChild();
35109 clayout.items = clayout.items || [];
35113 // replace this exitems with the clayout ones..
35114 xitems = clayout.items;
35116 // force background off if it's in center...
35117 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35118 cfg.background = false;
35120 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35123 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35124 //console.log('adding nested layout panel ' + cfg.toSource());
35125 this.add(region, ret);
35126 nb = {}; /// find first...
35131 // needs grid and region
35133 //var el = this.getRegion(region).el.createChild();
35135 *var el = this.el.createChild();
35136 // create the grid first...
35137 cfg.grid.container = el;
35138 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35141 if (region == 'center' && this.active ) {
35142 cfg.background = false;
35145 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35147 this.add(region, ret);
35149 if (cfg.background) {
35150 // render grid on panel activation (if panel background)
35151 ret.on('activate', function(gp) {
35152 if (!gp.grid.rendered) {
35153 // gp.grid.render(el);
35157 // cfg.grid.render(el);
35163 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35164 // it was the old xcomponent building that caused this before.
35165 // espeically if border is the top element in the tree.
35175 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35177 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35178 this.add(region, ret);
35182 throw "Can not add '" + cfg.xtype + "' to Border";
35188 this.beginUpdate();
35192 Roo.each(xitems, function(i) {
35193 region = nb && i.region ? i.region : false;
35195 var add = ret.addxtype(i);
35198 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35199 if (!i.background) {
35200 abn[region] = nb[region] ;
35207 // make the last non-background panel active..
35208 //if (nb) { Roo.log(abn); }
35211 for(var r in abn) {
35212 region = this.getRegion(r);
35214 // tried using nb[r], but it does not work..
35216 region.showPanel(abn[r]);
35227 factory : function(cfg)
35230 var validRegions = Roo.bootstrap.layout.Border.regions;
35232 var target = cfg.region;
35235 var r = Roo.bootstrap.layout;
35239 return new r.North(cfg);
35241 return new r.South(cfg);
35243 return new r.East(cfg);
35245 return new r.West(cfg);
35247 return new r.Center(cfg);
35249 throw 'Layout region "'+target+'" not supported.';
35256 * Ext JS Library 1.1.1
35257 * Copyright(c) 2006-2007, Ext JS, LLC.
35259 * Originally Released Under LGPL - original licence link has changed is not relivant.
35262 * <script type="text/javascript">
35266 * @class Roo.bootstrap.layout.Basic
35267 * @extends Roo.util.Observable
35268 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35269 * and does not have a titlebar, tabs or any other features. All it does is size and position
35270 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35271 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35272 * @cfg {string} region the region that it inhabits..
35273 * @cfg {bool} skipConfig skip config?
35277 Roo.bootstrap.layout.Basic = function(config){
35279 this.mgr = config.mgr;
35281 this.position = config.region;
35283 var skipConfig = config.skipConfig;
35287 * @scope Roo.BasicLayoutRegion
35291 * @event beforeremove
35292 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35293 * @param {Roo.LayoutRegion} this
35294 * @param {Roo.ContentPanel} panel The panel
35295 * @param {Object} e The cancel event object
35297 "beforeremove" : true,
35299 * @event invalidated
35300 * Fires when the layout for this region is changed.
35301 * @param {Roo.LayoutRegion} this
35303 "invalidated" : true,
35305 * @event visibilitychange
35306 * Fires when this region is shown or hidden
35307 * @param {Roo.LayoutRegion} this
35308 * @param {Boolean} visibility true or false
35310 "visibilitychange" : true,
35312 * @event paneladded
35313 * Fires when a panel is added.
35314 * @param {Roo.LayoutRegion} this
35315 * @param {Roo.ContentPanel} panel The panel
35317 "paneladded" : true,
35319 * @event panelremoved
35320 * Fires when a panel is removed.
35321 * @param {Roo.LayoutRegion} this
35322 * @param {Roo.ContentPanel} panel The panel
35324 "panelremoved" : true,
35326 * @event beforecollapse
35327 * Fires when this region before collapse.
35328 * @param {Roo.LayoutRegion} this
35330 "beforecollapse" : true,
35333 * Fires when this region is collapsed.
35334 * @param {Roo.LayoutRegion} this
35336 "collapsed" : true,
35339 * Fires when this region is expanded.
35340 * @param {Roo.LayoutRegion} this
35345 * Fires when this region is slid into view.
35346 * @param {Roo.LayoutRegion} this
35348 "slideshow" : true,
35351 * Fires when this region slides out of view.
35352 * @param {Roo.LayoutRegion} this
35354 "slidehide" : true,
35356 * @event panelactivated
35357 * Fires when a panel is activated.
35358 * @param {Roo.LayoutRegion} this
35359 * @param {Roo.ContentPanel} panel The activated panel
35361 "panelactivated" : true,
35364 * Fires when the user resizes this region.
35365 * @param {Roo.LayoutRegion} this
35366 * @param {Number} newSize The new size (width for east/west, height for north/south)
35370 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35371 this.panels = new Roo.util.MixedCollection();
35372 this.panels.getKey = this.getPanelId.createDelegate(this);
35374 this.activePanel = null;
35375 // ensure listeners are added...
35377 if (config.listeners || config.events) {
35378 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35379 listeners : config.listeners || {},
35380 events : config.events || {}
35384 if(skipConfig !== true){
35385 this.applyConfig(config);
35389 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35391 getPanelId : function(p){
35395 applyConfig : function(config){
35396 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35397 this.config = config;
35402 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35403 * the width, for horizontal (north, south) the height.
35404 * @param {Number} newSize The new width or height
35406 resizeTo : function(newSize){
35407 var el = this.el ? this.el :
35408 (this.activePanel ? this.activePanel.getEl() : null);
35410 switch(this.position){
35413 el.setWidth(newSize);
35414 this.fireEvent("resized", this, newSize);
35418 el.setHeight(newSize);
35419 this.fireEvent("resized", this, newSize);
35425 getBox : function(){
35426 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35429 getMargins : function(){
35430 return this.margins;
35433 updateBox : function(box){
35435 var el = this.activePanel.getEl();
35436 el.dom.style.left = box.x + "px";
35437 el.dom.style.top = box.y + "px";
35438 this.activePanel.setSize(box.width, box.height);
35442 * Returns the container element for this region.
35443 * @return {Roo.Element}
35445 getEl : function(){
35446 return this.activePanel;
35450 * Returns true if this region is currently visible.
35451 * @return {Boolean}
35453 isVisible : function(){
35454 return this.activePanel ? true : false;
35457 setActivePanel : function(panel){
35458 panel = this.getPanel(panel);
35459 if(this.activePanel && this.activePanel != panel){
35460 this.activePanel.setActiveState(false);
35461 this.activePanel.getEl().setLeftTop(-10000,-10000);
35463 this.activePanel = panel;
35464 panel.setActiveState(true);
35466 panel.setSize(this.box.width, this.box.height);
35468 this.fireEvent("panelactivated", this, panel);
35469 this.fireEvent("invalidated");
35473 * Show the specified panel.
35474 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35475 * @return {Roo.ContentPanel} The shown panel or null
35477 showPanel : function(panel){
35478 panel = this.getPanel(panel);
35480 this.setActivePanel(panel);
35486 * Get the active panel for this region.
35487 * @return {Roo.ContentPanel} The active panel or null
35489 getActivePanel : function(){
35490 return this.activePanel;
35494 * Add the passed ContentPanel(s)
35495 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35496 * @return {Roo.ContentPanel} The panel added (if only one was added)
35498 add : function(panel){
35499 if(arguments.length > 1){
35500 for(var i = 0, len = arguments.length; i < len; i++) {
35501 this.add(arguments[i]);
35505 if(this.hasPanel(panel)){
35506 this.showPanel(panel);
35509 var el = panel.getEl();
35510 if(el.dom.parentNode != this.mgr.el.dom){
35511 this.mgr.el.dom.appendChild(el.dom);
35513 if(panel.setRegion){
35514 panel.setRegion(this);
35516 this.panels.add(panel);
35517 el.setStyle("position", "absolute");
35518 if(!panel.background){
35519 this.setActivePanel(panel);
35520 if(this.config.initialSize && this.panels.getCount()==1){
35521 this.resizeTo(this.config.initialSize);
35524 this.fireEvent("paneladded", this, panel);
35529 * Returns true if the panel is in this region.
35530 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35531 * @return {Boolean}
35533 hasPanel : function(panel){
35534 if(typeof panel == "object"){ // must be panel obj
35535 panel = panel.getId();
35537 return this.getPanel(panel) ? true : false;
35541 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35542 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35543 * @param {Boolean} preservePanel Overrides the config preservePanel option
35544 * @return {Roo.ContentPanel} The panel that was removed
35546 remove : function(panel, preservePanel){
35547 panel = this.getPanel(panel);
35552 this.fireEvent("beforeremove", this, panel, e);
35553 if(e.cancel === true){
35556 var panelId = panel.getId();
35557 this.panels.removeKey(panelId);
35562 * Returns the panel specified or null if it's not in this region.
35563 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35564 * @return {Roo.ContentPanel}
35566 getPanel : function(id){
35567 if(typeof id == "object"){ // must be panel obj
35570 return this.panels.get(id);
35574 * Returns this regions position (north/south/east/west/center).
35577 getPosition: function(){
35578 return this.position;
35582 * Ext JS Library 1.1.1
35583 * Copyright(c) 2006-2007, Ext JS, LLC.
35585 * Originally Released Under LGPL - original licence link has changed is not relivant.
35588 * <script type="text/javascript">
35592 * @class Roo.bootstrap.layout.Region
35593 * @extends Roo.bootstrap.layout.Basic
35594 * This class represents a region in a layout manager.
35596 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35597 * @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})
35598 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35599 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35600 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35601 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35602 * @cfg {String} title The title for the region (overrides panel titles)
35603 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35604 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35605 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35606 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35607 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35608 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35609 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35610 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35611 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35612 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35614 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35615 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35616 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35617 * @cfg {Number} width For East/West panels
35618 * @cfg {Number} height For North/South panels
35619 * @cfg {Boolean} split To show the splitter
35620 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35622 * @cfg {string} cls Extra CSS classes to add to region
35624 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35625 * @cfg {string} region the region that it inhabits..
35628 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35629 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35631 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35632 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35633 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35635 Roo.bootstrap.layout.Region = function(config)
35637 this.applyConfig(config);
35639 var mgr = config.mgr;
35640 var pos = config.region;
35641 config.skipConfig = true;
35642 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35645 this.onRender(mgr.el);
35648 this.visible = true;
35649 this.collapsed = false;
35650 this.unrendered_panels = [];
35653 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35655 position: '', // set by wrapper (eg. north/south etc..)
35656 unrendered_panels : null, // unrendered panels.
35657 createBody : function(){
35658 /** This region's body element
35659 * @type Roo.Element */
35660 this.bodyEl = this.el.createChild({
35662 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35666 onRender: function(ctr, pos)
35668 var dh = Roo.DomHelper;
35669 /** This region's container element
35670 * @type Roo.Element */
35671 this.el = dh.append(ctr.dom, {
35673 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35675 /** This region's title element
35676 * @type Roo.Element */
35678 this.titleEl = dh.append(this.el.dom,
35681 unselectable: "on",
35682 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35684 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35685 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35688 this.titleEl.enableDisplayMode();
35689 /** This region's title text element
35690 * @type HTMLElement */
35691 this.titleTextEl = this.titleEl.dom.firstChild;
35692 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35694 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35695 this.closeBtn.enableDisplayMode();
35696 this.closeBtn.on("click", this.closeClicked, this);
35697 this.closeBtn.hide();
35699 this.createBody(this.config);
35700 if(this.config.hideWhenEmpty){
35702 this.on("paneladded", this.validateVisibility, this);
35703 this.on("panelremoved", this.validateVisibility, this);
35705 if(this.autoScroll){
35706 this.bodyEl.setStyle("overflow", "auto");
35708 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35710 //if(c.titlebar !== false){
35711 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35712 this.titleEl.hide();
35714 this.titleEl.show();
35715 if(this.config.title){
35716 this.titleTextEl.innerHTML = this.config.title;
35720 if(this.config.collapsed){
35721 this.collapse(true);
35723 if(this.config.hidden){
35727 if (this.unrendered_panels && this.unrendered_panels.length) {
35728 for (var i =0;i< this.unrendered_panels.length; i++) {
35729 this.add(this.unrendered_panels[i]);
35731 this.unrendered_panels = null;
35737 applyConfig : function(c)
35740 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35741 var dh = Roo.DomHelper;
35742 if(c.titlebar !== false){
35743 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35744 this.collapseBtn.on("click", this.collapse, this);
35745 this.collapseBtn.enableDisplayMode();
35747 if(c.showPin === true || this.showPin){
35748 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35749 this.stickBtn.enableDisplayMode();
35750 this.stickBtn.on("click", this.expand, this);
35751 this.stickBtn.hide();
35756 /** This region's collapsed element
35757 * @type Roo.Element */
35760 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35761 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35764 if(c.floatable !== false){
35765 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35766 this.collapsedEl.on("click", this.collapseClick, this);
35769 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35770 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35771 id: "message", unselectable: "on", style:{"float":"left"}});
35772 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35774 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35775 this.expandBtn.on("click", this.expand, this);
35779 if(this.collapseBtn){
35780 this.collapseBtn.setVisible(c.collapsible == true);
35783 this.cmargins = c.cmargins || this.cmargins ||
35784 (this.position == "west" || this.position == "east" ?
35785 {top: 0, left: 2, right:2, bottom: 0} :
35786 {top: 2, left: 0, right:0, bottom: 2});
35788 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35791 this.bottomTabs = c.tabPosition != "top";
35793 this.autoScroll = c.autoScroll || false;
35798 this.duration = c.duration || .30;
35799 this.slideDuration = c.slideDuration || .45;
35804 * Returns true if this region is currently visible.
35805 * @return {Boolean}
35807 isVisible : function(){
35808 return this.visible;
35812 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35813 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35815 //setCollapsedTitle : function(title){
35816 // title = title || " ";
35817 // if(this.collapsedTitleTextEl){
35818 // this.collapsedTitleTextEl.innerHTML = title;
35822 getBox : function(){
35824 // if(!this.collapsed){
35825 b = this.el.getBox(false, true);
35827 // b = this.collapsedEl.getBox(false, true);
35832 getMargins : function(){
35833 return this.margins;
35834 //return this.collapsed ? this.cmargins : this.margins;
35837 highlight : function(){
35838 this.el.addClass("x-layout-panel-dragover");
35841 unhighlight : function(){
35842 this.el.removeClass("x-layout-panel-dragover");
35845 updateBox : function(box)
35847 if (!this.bodyEl) {
35848 return; // not rendered yet..
35852 if(!this.collapsed){
35853 this.el.dom.style.left = box.x + "px";
35854 this.el.dom.style.top = box.y + "px";
35855 this.updateBody(box.width, box.height);
35857 this.collapsedEl.dom.style.left = box.x + "px";
35858 this.collapsedEl.dom.style.top = box.y + "px";
35859 this.collapsedEl.setSize(box.width, box.height);
35862 this.tabs.autoSizeTabs();
35866 updateBody : function(w, h)
35869 this.el.setWidth(w);
35870 w -= this.el.getBorderWidth("rl");
35871 if(this.config.adjustments){
35872 w += this.config.adjustments[0];
35875 if(h !== null && h > 0){
35876 this.el.setHeight(h);
35877 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35878 h -= this.el.getBorderWidth("tb");
35879 if(this.config.adjustments){
35880 h += this.config.adjustments[1];
35882 this.bodyEl.setHeight(h);
35884 h = this.tabs.syncHeight(h);
35887 if(this.panelSize){
35888 w = w !== null ? w : this.panelSize.width;
35889 h = h !== null ? h : this.panelSize.height;
35891 if(this.activePanel){
35892 var el = this.activePanel.getEl();
35893 w = w !== null ? w : el.getWidth();
35894 h = h !== null ? h : el.getHeight();
35895 this.panelSize = {width: w, height: h};
35896 this.activePanel.setSize(w, h);
35898 if(Roo.isIE && this.tabs){
35899 this.tabs.el.repaint();
35904 * Returns the container element for this region.
35905 * @return {Roo.Element}
35907 getEl : function(){
35912 * Hides this region.
35915 //if(!this.collapsed){
35916 this.el.dom.style.left = "-2000px";
35919 // this.collapsedEl.dom.style.left = "-2000px";
35920 // this.collapsedEl.hide();
35922 this.visible = false;
35923 this.fireEvent("visibilitychange", this, false);
35927 * Shows this region if it was previously hidden.
35930 //if(!this.collapsed){
35933 // this.collapsedEl.show();
35935 this.visible = true;
35936 this.fireEvent("visibilitychange", this, true);
35939 closeClicked : function(){
35940 if(this.activePanel){
35941 this.remove(this.activePanel);
35945 collapseClick : function(e){
35947 e.stopPropagation();
35950 e.stopPropagation();
35956 * Collapses this region.
35957 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35960 collapse : function(skipAnim, skipCheck = false){
35961 if(this.collapsed) {
35965 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35967 this.collapsed = true;
35969 this.split.el.hide();
35971 if(this.config.animate && skipAnim !== true){
35972 this.fireEvent("invalidated", this);
35973 this.animateCollapse();
35975 this.el.setLocation(-20000,-20000);
35977 this.collapsedEl.show();
35978 this.fireEvent("collapsed", this);
35979 this.fireEvent("invalidated", this);
35985 animateCollapse : function(){
35990 * Expands this region if it was previously collapsed.
35991 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35992 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35995 expand : function(e, skipAnim){
35997 e.stopPropagation();
35999 if(!this.collapsed || this.el.hasActiveFx()) {
36003 this.afterSlideIn();
36006 this.collapsed = false;
36007 if(this.config.animate && skipAnim !== true){
36008 this.animateExpand();
36012 this.split.el.show();
36014 this.collapsedEl.setLocation(-2000,-2000);
36015 this.collapsedEl.hide();
36016 this.fireEvent("invalidated", this);
36017 this.fireEvent("expanded", this);
36021 animateExpand : function(){
36025 initTabs : function()
36027 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36029 var ts = new Roo.bootstrap.panel.Tabs({
36030 el: this.bodyEl.dom,
36031 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36032 disableTooltips: this.config.disableTabTips,
36033 toolbar : this.config.toolbar
36036 if(this.config.hideTabs){
36037 ts.stripWrap.setDisplayed(false);
36040 ts.resizeTabs = this.config.resizeTabs === true;
36041 ts.minTabWidth = this.config.minTabWidth || 40;
36042 ts.maxTabWidth = this.config.maxTabWidth || 250;
36043 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36044 ts.monitorResize = false;
36045 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36046 ts.bodyEl.addClass('roo-layout-tabs-body');
36047 this.panels.each(this.initPanelAsTab, this);
36050 initPanelAsTab : function(panel){
36051 var ti = this.tabs.addTab(
36055 this.config.closeOnTab && panel.isClosable(),
36058 if(panel.tabTip !== undefined){
36059 ti.setTooltip(panel.tabTip);
36061 ti.on("activate", function(){
36062 this.setActivePanel(panel);
36065 if(this.config.closeOnTab){
36066 ti.on("beforeclose", function(t, e){
36068 this.remove(panel);
36072 panel.tabItem = ti;
36077 updatePanelTitle : function(panel, title)
36079 if(this.activePanel == panel){
36080 this.updateTitle(title);
36083 var ti = this.tabs.getTab(panel.getEl().id);
36085 if(panel.tabTip !== undefined){
36086 ti.setTooltip(panel.tabTip);
36091 updateTitle : function(title){
36092 if(this.titleTextEl && !this.config.title){
36093 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36097 setActivePanel : function(panel)
36099 panel = this.getPanel(panel);
36100 if(this.activePanel && this.activePanel != panel){
36101 if(this.activePanel.setActiveState(false) === false){
36105 this.activePanel = panel;
36106 panel.setActiveState(true);
36107 if(this.panelSize){
36108 panel.setSize(this.panelSize.width, this.panelSize.height);
36111 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36113 this.updateTitle(panel.getTitle());
36115 this.fireEvent("invalidated", this);
36117 this.fireEvent("panelactivated", this, panel);
36121 * Shows the specified panel.
36122 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36123 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36125 showPanel : function(panel)
36127 panel = this.getPanel(panel);
36130 var tab = this.tabs.getTab(panel.getEl().id);
36131 if(tab.isHidden()){
36132 this.tabs.unhideTab(tab.id);
36136 this.setActivePanel(panel);
36143 * Get the active panel for this region.
36144 * @return {Roo.ContentPanel} The active panel or null
36146 getActivePanel : function(){
36147 return this.activePanel;
36150 validateVisibility : function(){
36151 if(this.panels.getCount() < 1){
36152 this.updateTitle(" ");
36153 this.closeBtn.hide();
36156 if(!this.isVisible()){
36163 * Adds the passed ContentPanel(s) to this region.
36164 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36165 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36167 add : function(panel)
36169 if(arguments.length > 1){
36170 for(var i = 0, len = arguments.length; i < len; i++) {
36171 this.add(arguments[i]);
36176 // if we have not been rendered yet, then we can not really do much of this..
36177 if (!this.bodyEl) {
36178 this.unrendered_panels.push(panel);
36185 if(this.hasPanel(panel)){
36186 this.showPanel(panel);
36189 panel.setRegion(this);
36190 this.panels.add(panel);
36191 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36192 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36193 // and hide them... ???
36194 this.bodyEl.dom.appendChild(panel.getEl().dom);
36195 if(panel.background !== true){
36196 this.setActivePanel(panel);
36198 this.fireEvent("paneladded", this, panel);
36205 this.initPanelAsTab(panel);
36209 if(panel.background !== true){
36210 this.tabs.activate(panel.getEl().id);
36212 this.fireEvent("paneladded", this, panel);
36217 * Hides the tab for the specified panel.
36218 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36220 hidePanel : function(panel){
36221 if(this.tabs && (panel = this.getPanel(panel))){
36222 this.tabs.hideTab(panel.getEl().id);
36227 * Unhides the tab for a previously hidden panel.
36228 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36230 unhidePanel : function(panel){
36231 if(this.tabs && (panel = this.getPanel(panel))){
36232 this.tabs.unhideTab(panel.getEl().id);
36236 clearPanels : function(){
36237 while(this.panels.getCount() > 0){
36238 this.remove(this.panels.first());
36243 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36244 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36245 * @param {Boolean} preservePanel Overrides the config preservePanel option
36246 * @return {Roo.ContentPanel} The panel that was removed
36248 remove : function(panel, preservePanel)
36250 panel = this.getPanel(panel);
36255 this.fireEvent("beforeremove", this, panel, e);
36256 if(e.cancel === true){
36259 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36260 var panelId = panel.getId();
36261 this.panels.removeKey(panelId);
36263 document.body.appendChild(panel.getEl().dom);
36266 this.tabs.removeTab(panel.getEl().id);
36267 }else if (!preservePanel){
36268 this.bodyEl.dom.removeChild(panel.getEl().dom);
36270 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36271 var p = this.panels.first();
36272 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36273 tempEl.appendChild(p.getEl().dom);
36274 this.bodyEl.update("");
36275 this.bodyEl.dom.appendChild(p.getEl().dom);
36277 this.updateTitle(p.getTitle());
36279 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36280 this.setActivePanel(p);
36282 panel.setRegion(null);
36283 if(this.activePanel == panel){
36284 this.activePanel = null;
36286 if(this.config.autoDestroy !== false && preservePanel !== true){
36287 try{panel.destroy();}catch(e){}
36289 this.fireEvent("panelremoved", this, panel);
36294 * Returns the TabPanel component used by this region
36295 * @return {Roo.TabPanel}
36297 getTabs : function(){
36301 createTool : function(parentEl, className){
36302 var btn = Roo.DomHelper.append(parentEl, {
36304 cls: "x-layout-tools-button",
36307 cls: "roo-layout-tools-button-inner " + className,
36311 btn.addClassOnOver("roo-layout-tools-button-over");
36316 * Ext JS Library 1.1.1
36317 * Copyright(c) 2006-2007, Ext JS, LLC.
36319 * Originally Released Under LGPL - original licence link has changed is not relivant.
36322 * <script type="text/javascript">
36328 * @class Roo.SplitLayoutRegion
36329 * @extends Roo.LayoutRegion
36330 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36332 Roo.bootstrap.layout.Split = function(config){
36333 this.cursor = config.cursor;
36334 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36337 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36339 splitTip : "Drag to resize.",
36340 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36341 useSplitTips : false,
36343 applyConfig : function(config){
36344 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36347 onRender : function(ctr,pos) {
36349 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36350 if(!this.config.split){
36355 var splitEl = Roo.DomHelper.append(ctr.dom, {
36357 id: this.el.id + "-split",
36358 cls: "roo-layout-split roo-layout-split-"+this.position,
36361 /** The SplitBar for this region
36362 * @type Roo.SplitBar */
36363 // does not exist yet...
36364 Roo.log([this.position, this.orientation]);
36366 this.split = new Roo.bootstrap.SplitBar({
36367 dragElement : splitEl,
36368 resizingElement: this.el,
36369 orientation : this.orientation
36372 this.split.on("moved", this.onSplitMove, this);
36373 this.split.useShim = this.config.useShim === true;
36374 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36375 if(this.useSplitTips){
36376 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36378 //if(config.collapsible){
36379 // this.split.el.on("dblclick", this.collapse, this);
36382 if(typeof this.config.minSize != "undefined"){
36383 this.split.minSize = this.config.minSize;
36385 if(typeof this.config.maxSize != "undefined"){
36386 this.split.maxSize = this.config.maxSize;
36388 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36389 this.hideSplitter();
36394 getHMaxSize : function(){
36395 var cmax = this.config.maxSize || 10000;
36396 var center = this.mgr.getRegion("center");
36397 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36400 getVMaxSize : function(){
36401 var cmax = this.config.maxSize || 10000;
36402 var center = this.mgr.getRegion("center");
36403 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36406 onSplitMove : function(split, newSize){
36407 this.fireEvent("resized", this, newSize);
36411 * Returns the {@link Roo.SplitBar} for this region.
36412 * @return {Roo.SplitBar}
36414 getSplitBar : function(){
36419 this.hideSplitter();
36420 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36423 hideSplitter : function(){
36425 this.split.el.setLocation(-2000,-2000);
36426 this.split.el.hide();
36432 this.split.el.show();
36434 Roo.bootstrap.layout.Split.superclass.show.call(this);
36437 beforeSlide: function(){
36438 if(Roo.isGecko){// firefox overflow auto bug workaround
36439 this.bodyEl.clip();
36441 this.tabs.bodyEl.clip();
36443 if(this.activePanel){
36444 this.activePanel.getEl().clip();
36446 if(this.activePanel.beforeSlide){
36447 this.activePanel.beforeSlide();
36453 afterSlide : function(){
36454 if(Roo.isGecko){// firefox overflow auto bug workaround
36455 this.bodyEl.unclip();
36457 this.tabs.bodyEl.unclip();
36459 if(this.activePanel){
36460 this.activePanel.getEl().unclip();
36461 if(this.activePanel.afterSlide){
36462 this.activePanel.afterSlide();
36468 initAutoHide : function(){
36469 if(this.autoHide !== false){
36470 if(!this.autoHideHd){
36471 var st = new Roo.util.DelayedTask(this.slideIn, this);
36472 this.autoHideHd = {
36473 "mouseout": function(e){
36474 if(!e.within(this.el, true)){
36478 "mouseover" : function(e){
36484 this.el.on(this.autoHideHd);
36488 clearAutoHide : function(){
36489 if(this.autoHide !== false){
36490 this.el.un("mouseout", this.autoHideHd.mouseout);
36491 this.el.un("mouseover", this.autoHideHd.mouseover);
36495 clearMonitor : function(){
36496 Roo.get(document).un("click", this.slideInIf, this);
36499 // these names are backwards but not changed for compat
36500 slideOut : function(){
36501 if(this.isSlid || this.el.hasActiveFx()){
36504 this.isSlid = true;
36505 if(this.collapseBtn){
36506 this.collapseBtn.hide();
36508 this.closeBtnState = this.closeBtn.getStyle('display');
36509 this.closeBtn.hide();
36511 this.stickBtn.show();
36514 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36515 this.beforeSlide();
36516 this.el.setStyle("z-index", 10001);
36517 this.el.slideIn(this.getSlideAnchor(), {
36518 callback: function(){
36520 this.initAutoHide();
36521 Roo.get(document).on("click", this.slideInIf, this);
36522 this.fireEvent("slideshow", this);
36529 afterSlideIn : function(){
36530 this.clearAutoHide();
36531 this.isSlid = false;
36532 this.clearMonitor();
36533 this.el.setStyle("z-index", "");
36534 if(this.collapseBtn){
36535 this.collapseBtn.show();
36537 this.closeBtn.setStyle('display', this.closeBtnState);
36539 this.stickBtn.hide();
36541 this.fireEvent("slidehide", this);
36544 slideIn : function(cb){
36545 if(!this.isSlid || this.el.hasActiveFx()){
36549 this.isSlid = false;
36550 this.beforeSlide();
36551 this.el.slideOut(this.getSlideAnchor(), {
36552 callback: function(){
36553 this.el.setLeftTop(-10000, -10000);
36555 this.afterSlideIn();
36563 slideInIf : function(e){
36564 if(!e.within(this.el)){
36569 animateCollapse : function(){
36570 this.beforeSlide();
36571 this.el.setStyle("z-index", 20000);
36572 var anchor = this.getSlideAnchor();
36573 this.el.slideOut(anchor, {
36574 callback : function(){
36575 this.el.setStyle("z-index", "");
36576 this.collapsedEl.slideIn(anchor, {duration:.3});
36578 this.el.setLocation(-10000,-10000);
36580 this.fireEvent("collapsed", this);
36587 animateExpand : function(){
36588 this.beforeSlide();
36589 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36590 this.el.setStyle("z-index", 20000);
36591 this.collapsedEl.hide({
36594 this.el.slideIn(this.getSlideAnchor(), {
36595 callback : function(){
36596 this.el.setStyle("z-index", "");
36599 this.split.el.show();
36601 this.fireEvent("invalidated", this);
36602 this.fireEvent("expanded", this);
36630 getAnchor : function(){
36631 return this.anchors[this.position];
36634 getCollapseAnchor : function(){
36635 return this.canchors[this.position];
36638 getSlideAnchor : function(){
36639 return this.sanchors[this.position];
36642 getAlignAdj : function(){
36643 var cm = this.cmargins;
36644 switch(this.position){
36660 getExpandAdj : function(){
36661 var c = this.collapsedEl, cm = this.cmargins;
36662 switch(this.position){
36664 return [-(cm.right+c.getWidth()+cm.left), 0];
36667 return [cm.right+c.getWidth()+cm.left, 0];
36670 return [0, -(cm.top+cm.bottom+c.getHeight())];
36673 return [0, cm.top+cm.bottom+c.getHeight()];
36679 * Ext JS Library 1.1.1
36680 * Copyright(c) 2006-2007, Ext JS, LLC.
36682 * Originally Released Under LGPL - original licence link has changed is not relivant.
36685 * <script type="text/javascript">
36688 * These classes are private internal classes
36690 Roo.bootstrap.layout.Center = function(config){
36691 config.region = "center";
36692 Roo.bootstrap.layout.Region.call(this, config);
36693 this.visible = true;
36694 this.minWidth = config.minWidth || 20;
36695 this.minHeight = config.minHeight || 20;
36698 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36700 // center panel can't be hidden
36704 // center panel can't be hidden
36707 getMinWidth: function(){
36708 return this.minWidth;
36711 getMinHeight: function(){
36712 return this.minHeight;
36725 Roo.bootstrap.layout.North = function(config)
36727 config.region = 'north';
36728 config.cursor = 'n-resize';
36730 Roo.bootstrap.layout.Split.call(this, config);
36734 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36735 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36736 this.split.el.addClass("roo-layout-split-v");
36738 var size = config.initialSize || config.height;
36739 if(typeof size != "undefined"){
36740 this.el.setHeight(size);
36743 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36745 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36749 getBox : function(){
36750 if(this.collapsed){
36751 return this.collapsedEl.getBox();
36753 var box = this.el.getBox();
36755 box.height += this.split.el.getHeight();
36760 updateBox : function(box){
36761 if(this.split && !this.collapsed){
36762 box.height -= this.split.el.getHeight();
36763 this.split.el.setLeft(box.x);
36764 this.split.el.setTop(box.y+box.height);
36765 this.split.el.setWidth(box.width);
36767 if(this.collapsed){
36768 this.updateBody(box.width, null);
36770 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36778 Roo.bootstrap.layout.South = function(config){
36779 config.region = 'south';
36780 config.cursor = 's-resize';
36781 Roo.bootstrap.layout.Split.call(this, config);
36783 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36784 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36785 this.split.el.addClass("roo-layout-split-v");
36787 var size = config.initialSize || config.height;
36788 if(typeof size != "undefined"){
36789 this.el.setHeight(size);
36793 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36794 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36795 getBox : function(){
36796 if(this.collapsed){
36797 return this.collapsedEl.getBox();
36799 var box = this.el.getBox();
36801 var sh = this.split.el.getHeight();
36808 updateBox : function(box){
36809 if(this.split && !this.collapsed){
36810 var sh = this.split.el.getHeight();
36813 this.split.el.setLeft(box.x);
36814 this.split.el.setTop(box.y-sh);
36815 this.split.el.setWidth(box.width);
36817 if(this.collapsed){
36818 this.updateBody(box.width, null);
36820 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36824 Roo.bootstrap.layout.East = function(config){
36825 config.region = "east";
36826 config.cursor = "e-resize";
36827 Roo.bootstrap.layout.Split.call(this, config);
36829 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36830 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36831 this.split.el.addClass("roo-layout-split-h");
36833 var size = config.initialSize || config.width;
36834 if(typeof size != "undefined"){
36835 this.el.setWidth(size);
36838 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36839 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36840 getBox : function(){
36841 if(this.collapsed){
36842 return this.collapsedEl.getBox();
36844 var box = this.el.getBox();
36846 var sw = this.split.el.getWidth();
36853 updateBox : function(box){
36854 if(this.split && !this.collapsed){
36855 var sw = this.split.el.getWidth();
36857 this.split.el.setLeft(box.x);
36858 this.split.el.setTop(box.y);
36859 this.split.el.setHeight(box.height);
36862 if(this.collapsed){
36863 this.updateBody(null, box.height);
36865 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36869 Roo.bootstrap.layout.West = function(config){
36870 config.region = "west";
36871 config.cursor = "w-resize";
36873 Roo.bootstrap.layout.Split.call(this, config);
36875 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36876 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36877 this.split.el.addClass("roo-layout-split-h");
36881 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36882 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36884 onRender: function(ctr, pos)
36886 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36887 var size = this.config.initialSize || this.config.width;
36888 if(typeof size != "undefined"){
36889 this.el.setWidth(size);
36893 getBox : function(){
36894 if(this.collapsed){
36895 return this.collapsedEl.getBox();
36897 var box = this.el.getBox();
36899 box.width += this.split.el.getWidth();
36904 updateBox : function(box){
36905 if(this.split && !this.collapsed){
36906 var sw = this.split.el.getWidth();
36908 this.split.el.setLeft(box.x+box.width);
36909 this.split.el.setTop(box.y);
36910 this.split.el.setHeight(box.height);
36912 if(this.collapsed){
36913 this.updateBody(null, box.height);
36915 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36918 Roo.namespace("Roo.bootstrap.panel");/*
36920 * Ext JS Library 1.1.1
36921 * Copyright(c) 2006-2007, Ext JS, LLC.
36923 * Originally Released Under LGPL - original licence link has changed is not relivant.
36926 * <script type="text/javascript">
36929 * @class Roo.ContentPanel
36930 * @extends Roo.util.Observable
36931 * A basic ContentPanel element.
36932 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36933 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36934 * @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
36935 * @cfg {Boolean} closable True if the panel can be closed/removed
36936 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36937 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36938 * @cfg {Toolbar} toolbar A toolbar for this panel
36939 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36940 * @cfg {String} title The title for this panel
36941 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36942 * @cfg {String} url Calls {@link #setUrl} with this value
36943 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36944 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36945 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36946 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36947 * @cfg {Boolean} badges render the badges
36950 * Create a new ContentPanel.
36951 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36952 * @param {String/Object} config A string to set only the title or a config object
36953 * @param {String} content (optional) Set the HTML content for this panel
36954 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36956 Roo.bootstrap.panel.Content = function( config){
36958 this.tpl = config.tpl || false;
36960 var el = config.el;
36961 var content = config.content;
36963 if(config.autoCreate){ // xtype is available if this is called from factory
36966 this.el = Roo.get(el);
36967 if(!this.el && config && config.autoCreate){
36968 if(typeof config.autoCreate == "object"){
36969 if(!config.autoCreate.id){
36970 config.autoCreate.id = config.id||el;
36972 this.el = Roo.DomHelper.append(document.body,
36973 config.autoCreate, true);
36975 var elcfg = { tag: "div",
36976 cls: "roo-layout-inactive-content",
36980 elcfg.html = config.html;
36984 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36987 this.closable = false;
36988 this.loaded = false;
36989 this.active = false;
36992 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36994 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36996 this.wrapEl = this.el; //this.el.wrap();
36998 if (config.toolbar.items) {
36999 ti = config.toolbar.items ;
37000 delete config.toolbar.items ;
37004 this.toolbar.render(this.wrapEl, 'before');
37005 for(var i =0;i < ti.length;i++) {
37006 // Roo.log(['add child', items[i]]);
37007 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37009 this.toolbar.items = nitems;
37010 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37011 delete config.toolbar;
37015 // xtype created footer. - not sure if will work as we normally have to render first..
37016 if (this.footer && !this.footer.el && this.footer.xtype) {
37017 if (!this.wrapEl) {
37018 this.wrapEl = this.el.wrap();
37021 this.footer.container = this.wrapEl.createChild();
37023 this.footer = Roo.factory(this.footer, Roo);
37028 if(typeof config == "string"){
37029 this.title = config;
37031 Roo.apply(this, config);
37035 this.resizeEl = Roo.get(this.resizeEl, true);
37037 this.resizeEl = this.el;
37039 // handle view.xtype
37047 * Fires when this panel is activated.
37048 * @param {Roo.ContentPanel} this
37052 * @event deactivate
37053 * Fires when this panel is activated.
37054 * @param {Roo.ContentPanel} this
37056 "deactivate" : true,
37060 * Fires when this panel is resized if fitToFrame is true.
37061 * @param {Roo.ContentPanel} this
37062 * @param {Number} width The width after any component adjustments
37063 * @param {Number} height The height after any component adjustments
37069 * Fires when this tab is created
37070 * @param {Roo.ContentPanel} this
37081 if(this.autoScroll){
37082 this.resizeEl.setStyle("overflow", "auto");
37084 // fix randome scrolling
37085 //this.el.on('scroll', function() {
37086 // Roo.log('fix random scolling');
37087 // this.scrollTo('top',0);
37090 content = content || this.content;
37092 this.setContent(content);
37094 if(config && config.url){
37095 this.setUrl(this.url, this.params, this.loadOnce);
37100 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37102 if (this.view && typeof(this.view.xtype) != 'undefined') {
37103 this.view.el = this.el.appendChild(document.createElement("div"));
37104 this.view = Roo.factory(this.view);
37105 this.view.render && this.view.render(false, '');
37109 this.fireEvent('render', this);
37112 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37116 setRegion : function(region){
37117 this.region = region;
37118 this.setActiveClass(region && !this.background);
37122 setActiveClass: function(state)
37125 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37126 this.el.setStyle('position','relative');
37128 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37129 this.el.setStyle('position', 'absolute');
37134 * Returns the toolbar for this Panel if one was configured.
37135 * @return {Roo.Toolbar}
37137 getToolbar : function(){
37138 return this.toolbar;
37141 setActiveState : function(active)
37143 this.active = active;
37144 this.setActiveClass(active);
37146 if(this.fireEvent("deactivate", this) === false){
37151 this.fireEvent("activate", this);
37155 * Updates this panel's element
37156 * @param {String} content The new content
37157 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37159 setContent : function(content, loadScripts){
37160 this.el.update(content, loadScripts);
37163 ignoreResize : function(w, h){
37164 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37167 this.lastSize = {width: w, height: h};
37172 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37173 * @return {Roo.UpdateManager} The UpdateManager
37175 getUpdateManager : function(){
37176 return this.el.getUpdateManager();
37179 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37180 * @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:
37183 url: "your-url.php",
37184 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37185 callback: yourFunction,
37186 scope: yourObject, //(optional scope)
37189 text: "Loading...",
37194 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37195 * 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.
37196 * @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}
37197 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37198 * @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.
37199 * @return {Roo.ContentPanel} this
37202 var um = this.el.getUpdateManager();
37203 um.update.apply(um, arguments);
37209 * 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.
37210 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37211 * @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)
37212 * @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)
37213 * @return {Roo.UpdateManager} The UpdateManager
37215 setUrl : function(url, params, loadOnce){
37216 if(this.refreshDelegate){
37217 this.removeListener("activate", this.refreshDelegate);
37219 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37220 this.on("activate", this.refreshDelegate);
37221 return this.el.getUpdateManager();
37224 _handleRefresh : function(url, params, loadOnce){
37225 if(!loadOnce || !this.loaded){
37226 var updater = this.el.getUpdateManager();
37227 updater.update(url, params, this._setLoaded.createDelegate(this));
37231 _setLoaded : function(){
37232 this.loaded = true;
37236 * Returns this panel's id
37239 getId : function(){
37244 * Returns this panel's element - used by regiosn to add.
37245 * @return {Roo.Element}
37247 getEl : function(){
37248 return this.wrapEl || this.el;
37253 adjustForComponents : function(width, height)
37255 //Roo.log('adjustForComponents ');
37256 if(this.resizeEl != this.el){
37257 width -= this.el.getFrameWidth('lr');
37258 height -= this.el.getFrameWidth('tb');
37261 var te = this.toolbar.getEl();
37262 te.setWidth(width);
37263 height -= te.getHeight();
37266 var te = this.footer.getEl();
37267 te.setWidth(width);
37268 height -= te.getHeight();
37272 if(this.adjustments){
37273 width += this.adjustments[0];
37274 height += this.adjustments[1];
37276 return {"width": width, "height": height};
37279 setSize : function(width, height){
37280 if(this.fitToFrame && !this.ignoreResize(width, height)){
37281 if(this.fitContainer && this.resizeEl != this.el){
37282 this.el.setSize(width, height);
37284 var size = this.adjustForComponents(width, height);
37285 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37286 this.fireEvent('resize', this, size.width, size.height);
37291 * Returns this panel's title
37294 getTitle : function(){
37296 if (typeof(this.title) != 'object') {
37301 for (var k in this.title) {
37302 if (!this.title.hasOwnProperty(k)) {
37306 if (k.indexOf('-') >= 0) {
37307 var s = k.split('-');
37308 for (var i = 0; i<s.length; i++) {
37309 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37312 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37319 * Set this panel's title
37320 * @param {String} title
37322 setTitle : function(title){
37323 this.title = title;
37325 this.region.updatePanelTitle(this, title);
37330 * Returns true is this panel was configured to be closable
37331 * @return {Boolean}
37333 isClosable : function(){
37334 return this.closable;
37337 beforeSlide : function(){
37339 this.resizeEl.clip();
37342 afterSlide : function(){
37344 this.resizeEl.unclip();
37348 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37349 * Will fail silently if the {@link #setUrl} method has not been called.
37350 * This does not activate the panel, just updates its content.
37352 refresh : function(){
37353 if(this.refreshDelegate){
37354 this.loaded = false;
37355 this.refreshDelegate();
37360 * Destroys this panel
37362 destroy : function(){
37363 this.el.removeAllListeners();
37364 var tempEl = document.createElement("span");
37365 tempEl.appendChild(this.el.dom);
37366 tempEl.innerHTML = "";
37372 * form - if the content panel contains a form - this is a reference to it.
37373 * @type {Roo.form.Form}
37377 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37378 * This contains a reference to it.
37384 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37394 * @param {Object} cfg Xtype definition of item to add.
37398 getChildContainer: function () {
37399 return this.getEl();
37404 var ret = new Roo.factory(cfg);
37409 if (cfg.xtype.match(/^Form$/)) {
37412 //if (this.footer) {
37413 // el = this.footer.container.insertSibling(false, 'before');
37415 el = this.el.createChild();
37418 this.form = new Roo.form.Form(cfg);
37421 if ( this.form.allItems.length) {
37422 this.form.render(el.dom);
37426 // should only have one of theses..
37427 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37428 // views.. should not be just added - used named prop 'view''
37430 cfg.el = this.el.appendChild(document.createElement("div"));
37433 var ret = new Roo.factory(cfg);
37435 ret.render && ret.render(false, ''); // render blank..
37445 * @class Roo.bootstrap.panel.Grid
37446 * @extends Roo.bootstrap.panel.Content
37448 * Create a new GridPanel.
37449 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37450 * @param {Object} config A the config object
37456 Roo.bootstrap.panel.Grid = function(config)
37460 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37461 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37463 config.el = this.wrapper;
37464 //this.el = this.wrapper;
37466 if (config.container) {
37467 // ctor'ed from a Border/panel.grid
37470 this.wrapper.setStyle("overflow", "hidden");
37471 this.wrapper.addClass('roo-grid-container');
37476 if(config.toolbar){
37477 var tool_el = this.wrapper.createChild();
37478 this.toolbar = Roo.factory(config.toolbar);
37480 if (config.toolbar.items) {
37481 ti = config.toolbar.items ;
37482 delete config.toolbar.items ;
37486 this.toolbar.render(tool_el);
37487 for(var i =0;i < ti.length;i++) {
37488 // Roo.log(['add child', items[i]]);
37489 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37491 this.toolbar.items = nitems;
37493 delete config.toolbar;
37496 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37497 config.grid.scrollBody = true;;
37498 config.grid.monitorWindowResize = false; // turn off autosizing
37499 config.grid.autoHeight = false;
37500 config.grid.autoWidth = false;
37502 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37504 if (config.background) {
37505 // render grid on panel activation (if panel background)
37506 this.on('activate', function(gp) {
37507 if (!gp.grid.rendered) {
37508 gp.grid.render(this.wrapper);
37509 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37514 this.grid.render(this.wrapper);
37515 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37518 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37519 // ??? needed ??? config.el = this.wrapper;
37524 // xtype created footer. - not sure if will work as we normally have to render first..
37525 if (this.footer && !this.footer.el && this.footer.xtype) {
37527 var ctr = this.grid.getView().getFooterPanel(true);
37528 this.footer.dataSource = this.grid.dataSource;
37529 this.footer = Roo.factory(this.footer, Roo);
37530 this.footer.render(ctr);
37540 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37541 getId : function(){
37542 return this.grid.id;
37546 * Returns the grid for this panel
37547 * @return {Roo.bootstrap.Table}
37549 getGrid : function(){
37553 setSize : function(width, height){
37554 if(!this.ignoreResize(width, height)){
37555 var grid = this.grid;
37556 var size = this.adjustForComponents(width, height);
37557 var gridel = grid.getGridEl();
37558 gridel.setSize(size.width, size.height);
37560 var thd = grid.getGridEl().select('thead',true).first();
37561 var tbd = grid.getGridEl().select('tbody', true).first();
37563 tbd.setSize(width, height - thd.getHeight());
37572 beforeSlide : function(){
37573 this.grid.getView().scroller.clip();
37576 afterSlide : function(){
37577 this.grid.getView().scroller.unclip();
37580 destroy : function(){
37581 this.grid.destroy();
37583 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37588 * @class Roo.bootstrap.panel.Nest
37589 * @extends Roo.bootstrap.panel.Content
37591 * Create a new Panel, that can contain a layout.Border.
37594 * @param {Roo.BorderLayout} layout The layout for this panel
37595 * @param {String/Object} config A string to set only the title or a config object
37597 Roo.bootstrap.panel.Nest = function(config)
37599 // construct with only one argument..
37600 /* FIXME - implement nicer consturctors
37601 if (layout.layout) {
37603 layout = config.layout;
37604 delete config.layout;
37606 if (layout.xtype && !layout.getEl) {
37607 // then layout needs constructing..
37608 layout = Roo.factory(layout, Roo);
37612 config.el = config.layout.getEl();
37614 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37616 config.layout.monitorWindowResize = false; // turn off autosizing
37617 this.layout = config.layout;
37618 this.layout.getEl().addClass("roo-layout-nested-layout");
37625 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37627 setSize : function(width, height){
37628 if(!this.ignoreResize(width, height)){
37629 var size = this.adjustForComponents(width, height);
37630 var el = this.layout.getEl();
37631 if (size.height < 1) {
37632 el.setWidth(size.width);
37634 el.setSize(size.width, size.height);
37636 var touch = el.dom.offsetWidth;
37637 this.layout.layout();
37638 // ie requires a double layout on the first pass
37639 if(Roo.isIE && !this.initialized){
37640 this.initialized = true;
37641 this.layout.layout();
37646 // activate all subpanels if not currently active..
37648 setActiveState : function(active){
37649 this.active = active;
37650 this.setActiveClass(active);
37653 this.fireEvent("deactivate", this);
37657 this.fireEvent("activate", this);
37658 // not sure if this should happen before or after..
37659 if (!this.layout) {
37660 return; // should not happen..
37663 for (var r in this.layout.regions) {
37664 reg = this.layout.getRegion(r);
37665 if (reg.getActivePanel()) {
37666 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37667 reg.setActivePanel(reg.getActivePanel());
37670 if (!reg.panels.length) {
37673 reg.showPanel(reg.getPanel(0));
37682 * Returns the nested BorderLayout for this panel
37683 * @return {Roo.BorderLayout}
37685 getLayout : function(){
37686 return this.layout;
37690 * Adds a xtype elements to the layout of the nested panel
37694 xtype : 'ContentPanel',
37701 xtype : 'NestedLayoutPanel',
37707 items : [ ... list of content panels or nested layout panels.. ]
37711 * @param {Object} cfg Xtype definition of item to add.
37713 addxtype : function(cfg) {
37714 return this.layout.addxtype(cfg);
37719 * Ext JS Library 1.1.1
37720 * Copyright(c) 2006-2007, Ext JS, LLC.
37722 * Originally Released Under LGPL - original licence link has changed is not relivant.
37725 * <script type="text/javascript">
37728 * @class Roo.TabPanel
37729 * @extends Roo.util.Observable
37730 * A lightweight tab container.
37734 // basic tabs 1, built from existing content
37735 var tabs = new Roo.TabPanel("tabs1");
37736 tabs.addTab("script", "View Script");
37737 tabs.addTab("markup", "View Markup");
37738 tabs.activate("script");
37740 // more advanced tabs, built from javascript
37741 var jtabs = new Roo.TabPanel("jtabs");
37742 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37744 // set up the UpdateManager
37745 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37746 var updater = tab2.getUpdateManager();
37747 updater.setDefaultUrl("ajax1.htm");
37748 tab2.on('activate', updater.refresh, updater, true);
37750 // Use setUrl for Ajax loading
37751 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37752 tab3.setUrl("ajax2.htm", null, true);
37755 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37758 jtabs.activate("jtabs-1");
37761 * Create a new TabPanel.
37762 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37763 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37765 Roo.bootstrap.panel.Tabs = function(config){
37767 * The container element for this TabPanel.
37768 * @type Roo.Element
37770 this.el = Roo.get(config.el);
37773 if(typeof config == "boolean"){
37774 this.tabPosition = config ? "bottom" : "top";
37776 Roo.apply(this, config);
37780 if(this.tabPosition == "bottom"){
37781 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37782 this.el.addClass("roo-tabs-bottom");
37784 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37785 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37786 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37788 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37790 if(this.tabPosition != "bottom"){
37791 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37792 * @type Roo.Element
37794 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37795 this.el.addClass("roo-tabs-top");
37799 this.bodyEl.setStyle("position", "relative");
37801 this.active = null;
37802 this.activateDelegate = this.activate.createDelegate(this);
37807 * Fires when the active tab changes
37808 * @param {Roo.TabPanel} this
37809 * @param {Roo.TabPanelItem} activePanel The new active tab
37813 * @event beforetabchange
37814 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37815 * @param {Roo.TabPanel} this
37816 * @param {Object} e Set cancel to true on this object to cancel the tab change
37817 * @param {Roo.TabPanelItem} tab The tab being changed to
37819 "beforetabchange" : true
37822 Roo.EventManager.onWindowResize(this.onResize, this);
37823 this.cpad = this.el.getPadding("lr");
37824 this.hiddenCount = 0;
37827 // toolbar on the tabbar support...
37828 if (this.toolbar) {
37829 alert("no toolbar support yet");
37830 this.toolbar = false;
37832 var tcfg = this.toolbar;
37833 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37834 this.toolbar = new Roo.Toolbar(tcfg);
37835 if (Roo.isSafari) {
37836 var tbl = tcfg.container.child('table', true);
37837 tbl.setAttribute('width', '100%');
37845 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37848 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37850 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37852 tabPosition : "top",
37854 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37856 currentTabWidth : 0,
37858 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37862 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37866 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37868 preferredTabWidth : 175,
37870 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37872 resizeTabs : false,
37874 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37876 monitorResize : true,
37878 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37883 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37884 * @param {String} id The id of the div to use <b>or create</b>
37885 * @param {String} text The text for the tab
37886 * @param {String} content (optional) Content to put in the TabPanelItem body
37887 * @param {Boolean} closable (optional) True to create a close icon on the tab
37888 * @return {Roo.TabPanelItem} The created TabPanelItem
37890 addTab : function(id, text, content, closable, tpl)
37892 var item = new Roo.bootstrap.panel.TabItem({
37896 closable : closable,
37899 this.addTabItem(item);
37901 item.setContent(content);
37907 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37908 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37909 * @return {Roo.TabPanelItem}
37911 getTab : function(id){
37912 return this.items[id];
37916 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37917 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37919 hideTab : function(id){
37920 var t = this.items[id];
37923 this.hiddenCount++;
37924 this.autoSizeTabs();
37929 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37930 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37932 unhideTab : function(id){
37933 var t = this.items[id];
37935 t.setHidden(false);
37936 this.hiddenCount--;
37937 this.autoSizeTabs();
37942 * Adds an existing {@link Roo.TabPanelItem}.
37943 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37945 addTabItem : function(item){
37946 this.items[item.id] = item;
37947 this.items.push(item);
37948 // if(this.resizeTabs){
37949 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37950 // this.autoSizeTabs();
37952 // item.autoSize();
37957 * Removes a {@link Roo.TabPanelItem}.
37958 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37960 removeTab : function(id){
37961 var items = this.items;
37962 var tab = items[id];
37963 if(!tab) { return; }
37964 var index = items.indexOf(tab);
37965 if(this.active == tab && items.length > 1){
37966 var newTab = this.getNextAvailable(index);
37971 this.stripEl.dom.removeChild(tab.pnode.dom);
37972 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37973 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37975 items.splice(index, 1);
37976 delete this.items[tab.id];
37977 tab.fireEvent("close", tab);
37978 tab.purgeListeners();
37979 this.autoSizeTabs();
37982 getNextAvailable : function(start){
37983 var items = this.items;
37985 // look for a next tab that will slide over to
37986 // replace the one being removed
37987 while(index < items.length){
37988 var item = items[++index];
37989 if(item && !item.isHidden()){
37993 // if one isn't found select the previous tab (on the left)
37996 var item = items[--index];
37997 if(item && !item.isHidden()){
38005 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38006 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38008 disableTab : function(id){
38009 var tab = this.items[id];
38010 if(tab && this.active != tab){
38016 * Enables a {@link Roo.TabPanelItem} that is disabled.
38017 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38019 enableTab : function(id){
38020 var tab = this.items[id];
38025 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38026 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38027 * @return {Roo.TabPanelItem} The TabPanelItem.
38029 activate : function(id){
38030 var tab = this.items[id];
38034 if(tab == this.active || tab.disabled){
38038 this.fireEvent("beforetabchange", this, e, tab);
38039 if(e.cancel !== true && !tab.disabled){
38041 this.active.hide();
38043 this.active = this.items[id];
38044 this.active.show();
38045 this.fireEvent("tabchange", this, this.active);
38051 * Gets the active {@link Roo.TabPanelItem}.
38052 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38054 getActiveTab : function(){
38055 return this.active;
38059 * Updates the tab body element to fit the height of the container element
38060 * for overflow scrolling
38061 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38063 syncHeight : function(targetHeight){
38064 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38065 var bm = this.bodyEl.getMargins();
38066 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38067 this.bodyEl.setHeight(newHeight);
38071 onResize : function(){
38072 if(this.monitorResize){
38073 this.autoSizeTabs();
38078 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38080 beginUpdate : function(){
38081 this.updating = true;
38085 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38087 endUpdate : function(){
38088 this.updating = false;
38089 this.autoSizeTabs();
38093 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38095 autoSizeTabs : function(){
38096 var count = this.items.length;
38097 var vcount = count - this.hiddenCount;
38098 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38101 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38102 var availWidth = Math.floor(w / vcount);
38103 var b = this.stripBody;
38104 if(b.getWidth() > w){
38105 var tabs = this.items;
38106 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38107 if(availWidth < this.minTabWidth){
38108 /*if(!this.sleft){ // incomplete scrolling code
38109 this.createScrollButtons();
38112 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38115 if(this.currentTabWidth < this.preferredTabWidth){
38116 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38122 * Returns the number of tabs in this TabPanel.
38125 getCount : function(){
38126 return this.items.length;
38130 * Resizes all the tabs to the passed width
38131 * @param {Number} The new width
38133 setTabWidth : function(width){
38134 this.currentTabWidth = width;
38135 for(var i = 0, len = this.items.length; i < len; i++) {
38136 if(!this.items[i].isHidden()) {
38137 this.items[i].setWidth(width);
38143 * Destroys this TabPanel
38144 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38146 destroy : function(removeEl){
38147 Roo.EventManager.removeResizeListener(this.onResize, this);
38148 for(var i = 0, len = this.items.length; i < len; i++){
38149 this.items[i].purgeListeners();
38151 if(removeEl === true){
38152 this.el.update("");
38157 createStrip : function(container)
38159 var strip = document.createElement("nav");
38160 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38161 container.appendChild(strip);
38165 createStripList : function(strip)
38167 // div wrapper for retard IE
38168 // returns the "tr" element.
38169 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38170 //'<div class="x-tabs-strip-wrap">'+
38171 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38172 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38173 return strip.firstChild; //.firstChild.firstChild.firstChild;
38175 createBody : function(container)
38177 var body = document.createElement("div");
38178 Roo.id(body, "tab-body");
38179 //Roo.fly(body).addClass("x-tabs-body");
38180 Roo.fly(body).addClass("tab-content");
38181 container.appendChild(body);
38184 createItemBody :function(bodyEl, id){
38185 var body = Roo.getDom(id);
38187 body = document.createElement("div");
38190 //Roo.fly(body).addClass("x-tabs-item-body");
38191 Roo.fly(body).addClass("tab-pane");
38192 bodyEl.insertBefore(body, bodyEl.firstChild);
38196 createStripElements : function(stripEl, text, closable, tpl)
38198 var td = document.createElement("li"); // was td..
38201 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38204 stripEl.appendChild(td);
38206 td.className = "x-tabs-closable";
38207 if(!this.closeTpl){
38208 this.closeTpl = new Roo.Template(
38209 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38210 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38211 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38214 var el = this.closeTpl.overwrite(td, {"text": text});
38215 var close = el.getElementsByTagName("div")[0];
38216 var inner = el.getElementsByTagName("em")[0];
38217 return {"el": el, "close": close, "inner": inner};
38220 // not sure what this is..
38221 // if(!this.tabTpl){
38222 //this.tabTpl = new Roo.Template(
38223 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38224 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38226 // this.tabTpl = new Roo.Template(
38227 // '<a href="#">' +
38228 // '<span unselectable="on"' +
38229 // (this.disableTooltips ? '' : ' title="{text}"') +
38230 // ' >{text}</span></a>'
38236 var template = tpl || this.tabTpl || false;
38240 template = new Roo.Template(
38242 '<span unselectable="on"' +
38243 (this.disableTooltips ? '' : ' title="{text}"') +
38244 ' >{text}</span></a>'
38248 switch (typeof(template)) {
38252 template = new Roo.Template(template);
38258 var el = template.overwrite(td, {"text": text});
38260 var inner = el.getElementsByTagName("span")[0];
38262 return {"el": el, "inner": inner};
38270 * @class Roo.TabPanelItem
38271 * @extends Roo.util.Observable
38272 * Represents an individual item (tab plus body) in a TabPanel.
38273 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38274 * @param {String} id The id of this TabPanelItem
38275 * @param {String} text The text for the tab of this TabPanelItem
38276 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38278 Roo.bootstrap.panel.TabItem = function(config){
38280 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38281 * @type Roo.TabPanel
38283 this.tabPanel = config.panel;
38285 * The id for this TabPanelItem
38288 this.id = config.id;
38290 this.disabled = false;
38292 this.text = config.text;
38294 this.loaded = false;
38295 this.closable = config.closable;
38298 * The body element for this TabPanelItem.
38299 * @type Roo.Element
38301 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38302 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38303 this.bodyEl.setStyle("display", "block");
38304 this.bodyEl.setStyle("zoom", "1");
38305 //this.hideAction();
38307 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38309 this.el = Roo.get(els.el);
38310 this.inner = Roo.get(els.inner, true);
38311 this.textEl = Roo.get(this.el.dom.firstChild, true);
38312 this.pnode = Roo.get(els.el.parentNode, true);
38313 // this.el.on("mousedown", this.onTabMouseDown, this);
38314 this.el.on("click", this.onTabClick, this);
38316 if(config.closable){
38317 var c = Roo.get(els.close, true);
38318 c.dom.title = this.closeText;
38319 c.addClassOnOver("close-over");
38320 c.on("click", this.closeClick, this);
38326 * Fires when this tab becomes the active tab.
38327 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38328 * @param {Roo.TabPanelItem} this
38332 * @event beforeclose
38333 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38334 * @param {Roo.TabPanelItem} this
38335 * @param {Object} e Set cancel to true on this object to cancel the close.
38337 "beforeclose": true,
38340 * Fires when this tab is closed.
38341 * @param {Roo.TabPanelItem} this
38345 * @event deactivate
38346 * Fires when this tab is no longer the active tab.
38347 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38348 * @param {Roo.TabPanelItem} this
38350 "deactivate" : true
38352 this.hidden = false;
38354 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38357 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38359 purgeListeners : function(){
38360 Roo.util.Observable.prototype.purgeListeners.call(this);
38361 this.el.removeAllListeners();
38364 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38367 this.pnode.addClass("active");
38370 this.tabPanel.stripWrap.repaint();
38372 this.fireEvent("activate", this.tabPanel, this);
38376 * Returns true if this tab is the active tab.
38377 * @return {Boolean}
38379 isActive : function(){
38380 return this.tabPanel.getActiveTab() == this;
38384 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38387 this.pnode.removeClass("active");
38389 this.fireEvent("deactivate", this.tabPanel, this);
38392 hideAction : function(){
38393 this.bodyEl.hide();
38394 this.bodyEl.setStyle("position", "absolute");
38395 this.bodyEl.setLeft("-20000px");
38396 this.bodyEl.setTop("-20000px");
38399 showAction : function(){
38400 this.bodyEl.setStyle("position", "relative");
38401 this.bodyEl.setTop("");
38402 this.bodyEl.setLeft("");
38403 this.bodyEl.show();
38407 * Set the tooltip for the tab.
38408 * @param {String} tooltip The tab's tooltip
38410 setTooltip : function(text){
38411 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38412 this.textEl.dom.qtip = text;
38413 this.textEl.dom.removeAttribute('title');
38415 this.textEl.dom.title = text;
38419 onTabClick : function(e){
38420 e.preventDefault();
38421 this.tabPanel.activate(this.id);
38424 onTabMouseDown : function(e){
38425 e.preventDefault();
38426 this.tabPanel.activate(this.id);
38429 getWidth : function(){
38430 return this.inner.getWidth();
38433 setWidth : function(width){
38434 var iwidth = width - this.pnode.getPadding("lr");
38435 this.inner.setWidth(iwidth);
38436 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38437 this.pnode.setWidth(width);
38441 * Show or hide the tab
38442 * @param {Boolean} hidden True to hide or false to show.
38444 setHidden : function(hidden){
38445 this.hidden = hidden;
38446 this.pnode.setStyle("display", hidden ? "none" : "");
38450 * Returns true if this tab is "hidden"
38451 * @return {Boolean}
38453 isHidden : function(){
38454 return this.hidden;
38458 * Returns the text for this tab
38461 getText : function(){
38465 autoSize : function(){
38466 //this.el.beginMeasure();
38467 this.textEl.setWidth(1);
38469 * #2804 [new] Tabs in Roojs
38470 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38472 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38473 //this.el.endMeasure();
38477 * Sets the text for the tab (Note: this also sets the tooltip text)
38478 * @param {String} text The tab's text and tooltip
38480 setText : function(text){
38482 this.textEl.update(text);
38483 this.setTooltip(text);
38484 //if(!this.tabPanel.resizeTabs){
38485 // this.autoSize();
38489 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38491 activate : function(){
38492 this.tabPanel.activate(this.id);
38496 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38498 disable : function(){
38499 if(this.tabPanel.active != this){
38500 this.disabled = true;
38501 this.pnode.addClass("disabled");
38506 * Enables this TabPanelItem if it was previously disabled.
38508 enable : function(){
38509 this.disabled = false;
38510 this.pnode.removeClass("disabled");
38514 * Sets the content for this TabPanelItem.
38515 * @param {String} content The content
38516 * @param {Boolean} loadScripts true to look for and load scripts
38518 setContent : function(content, loadScripts){
38519 this.bodyEl.update(content, loadScripts);
38523 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38524 * @return {Roo.UpdateManager} The UpdateManager
38526 getUpdateManager : function(){
38527 return this.bodyEl.getUpdateManager();
38531 * Set a URL to be used to load the content for this TabPanelItem.
38532 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38533 * @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)
38534 * @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)
38535 * @return {Roo.UpdateManager} The UpdateManager
38537 setUrl : function(url, params, loadOnce){
38538 if(this.refreshDelegate){
38539 this.un('activate', this.refreshDelegate);
38541 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38542 this.on("activate", this.refreshDelegate);
38543 return this.bodyEl.getUpdateManager();
38547 _handleRefresh : function(url, params, loadOnce){
38548 if(!loadOnce || !this.loaded){
38549 var updater = this.bodyEl.getUpdateManager();
38550 updater.update(url, params, this._setLoaded.createDelegate(this));
38555 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38556 * Will fail silently if the setUrl method has not been called.
38557 * This does not activate the panel, just updates its content.
38559 refresh : function(){
38560 if(this.refreshDelegate){
38561 this.loaded = false;
38562 this.refreshDelegate();
38567 _setLoaded : function(){
38568 this.loaded = true;
38572 closeClick : function(e){
38575 this.fireEvent("beforeclose", this, o);
38576 if(o.cancel !== true){
38577 this.tabPanel.removeTab(this.id);
38581 * The text displayed in the tooltip for the close icon.
38584 closeText : "Close this tab"
38587 * This script refer to:
38588 * Title: International Telephone Input
38589 * Author: Jack O'Connor
38590 * Code version: v12.1.12
38591 * Availability: https://github.com/jackocnr/intl-tel-input.git
38594 Roo.bootstrap.PhoneInputData = function() {
38597 "Afghanistan (افغانستان)",
38602 "Albania (Shqipëri)",
38607 "Algeria (الجزائر)",
38632 "Antigua and Barbuda",
38642 "Armenia (Հայաստան)",
38658 "Austria (Österreich)",
38663 "Azerbaijan (Azərbaycan)",
38673 "Bahrain (البحرين)",
38678 "Bangladesh (বাংলাদেশ)",
38688 "Belarus (Беларусь)",
38693 "Belgium (België)",
38723 "Bosnia and Herzegovina (Босна и Херцеговина)",
38738 "British Indian Ocean Territory",
38743 "British Virgin Islands",
38753 "Bulgaria (България)",
38763 "Burundi (Uburundi)",
38768 "Cambodia (កម្ពុជា)",
38773 "Cameroon (Cameroun)",
38782 ["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"]
38785 "Cape Verde (Kabu Verdi)",
38790 "Caribbean Netherlands",
38801 "Central African Republic (République centrafricaine)",
38821 "Christmas Island",
38827 "Cocos (Keeling) Islands",
38838 "Comoros (جزر القمر)",
38843 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38848 "Congo (Republic) (Congo-Brazzaville)",
38868 "Croatia (Hrvatska)",
38889 "Czech Republic (Česká republika)",
38894 "Denmark (Danmark)",
38909 "Dominican Republic (República Dominicana)",
38913 ["809", "829", "849"]
38931 "Equatorial Guinea (Guinea Ecuatorial)",
38951 "Falkland Islands (Islas Malvinas)",
38956 "Faroe Islands (Føroyar)",
38977 "French Guiana (Guyane française)",
38982 "French Polynesia (Polynésie française)",
38997 "Georgia (საქართველო)",
39002 "Germany (Deutschland)",
39022 "Greenland (Kalaallit Nunaat)",
39059 "Guinea-Bissau (Guiné Bissau)",
39084 "Hungary (Magyarország)",
39089 "Iceland (Ísland)",
39109 "Iraq (العراق)",
39125 "Israel (ישראל)",
39152 "Jordan (الأردن)",
39157 "Kazakhstan (Казахстан)",
39178 "Kuwait (الكويت)",
39183 "Kyrgyzstan (Кыргызстан)",
39193 "Latvia (Latvija)",
39198 "Lebanon (لبنان)",
39213 "Libya (ليبيا)",
39223 "Lithuania (Lietuva)",
39238 "Macedonia (FYROM) (Македонија)",
39243 "Madagascar (Madagasikara)",
39273 "Marshall Islands",
39283 "Mauritania (موريتانيا)",
39288 "Mauritius (Moris)",
39309 "Moldova (Republica Moldova)",
39319 "Mongolia (Монгол)",
39324 "Montenegro (Crna Gora)",
39334 "Morocco (المغرب)",
39340 "Mozambique (Moçambique)",
39345 "Myanmar (Burma) (မြန်မာ)",
39350 "Namibia (Namibië)",
39365 "Netherlands (Nederland)",
39370 "New Caledonia (Nouvelle-Calédonie)",
39405 "North Korea (조선 민주주의 인민 공화국)",
39410 "Northern Mariana Islands",
39426 "Pakistan (پاکستان)",
39436 "Palestine (فلسطين)",
39446 "Papua New Guinea",
39488 "Réunion (La Réunion)",
39494 "Romania (România)",
39510 "Saint Barthélemy",
39521 "Saint Kitts and Nevis",
39531 "Saint Martin (Saint-Martin (partie française))",
39537 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39542 "Saint Vincent and the Grenadines",
39557 "São Tomé and Príncipe (São Tomé e Príncipe)",
39562 "Saudi Arabia (المملكة العربية السعودية)",
39567 "Senegal (Sénégal)",
39597 "Slovakia (Slovensko)",
39602 "Slovenia (Slovenija)",
39612 "Somalia (Soomaaliya)",
39622 "South Korea (대한민국)",
39627 "South Sudan (جنوب السودان)",
39637 "Sri Lanka (ශ්රී ලංකාව)",
39642 "Sudan (السودان)",
39652 "Svalbard and Jan Mayen",
39663 "Sweden (Sverige)",
39668 "Switzerland (Schweiz)",
39673 "Syria (سوريا)",
39718 "Trinidad and Tobago",
39723 "Tunisia (تونس)",
39728 "Turkey (Türkiye)",
39738 "Turks and Caicos Islands",
39748 "U.S. Virgin Islands",
39758 "Ukraine (Україна)",
39763 "United Arab Emirates (الإمارات العربية المتحدة)",
39785 "Uzbekistan (Oʻzbekiston)",
39795 "Vatican City (Città del Vaticano)",
39806 "Vietnam (Việt Nam)",
39811 "Wallis and Futuna (Wallis-et-Futuna)",
39816 "Western Sahara (الصحراء الغربية)",
39822 "Yemen (اليمن)",
39846 * This script refer to:
39847 * Title: International Telephone Input
39848 * Author: Jack O'Connor
39849 * Code version: v12.1.12
39850 * Availability: https://github.com/jackocnr/intl-tel-input.git
39854 * @class Roo.bootstrap.PhoneInput
39855 * @extends Roo.bootstrap.TriggerField
39856 * An input with International dial-code selection
39858 * @cfg {String} defaultDialCode default '+852'
39859 * @cfg {Array} preferedCountries default []
39862 * Create a new PhoneInput.
39863 * @param {Object} config Configuration options
39866 Roo.bootstrap.PhoneInput = function(config) {
39867 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39870 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39872 listWidth: undefined,
39874 selectedClass: 'active',
39876 invalidClass : "has-warning",
39878 validClass: 'has-success',
39880 allowed: '0123456789',
39883 * @cfg {String} defaultDialCode The default dial code when initializing the input
39885 defaultDialCode: '+852',
39888 * @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
39890 preferedCountries: false,
39892 getAutoCreate : function()
39894 var data = Roo.bootstrap.PhoneInputData();
39895 var align = this.labelAlign || this.parentLabelAlign();
39898 this.allCountries = [];
39899 this.dialCodeMapping = [];
39901 for (var i = 0; i < data.length; i++) {
39903 this.allCountries[i] = {
39907 priority: c[3] || 0,
39908 areaCodes: c[4] || null
39910 this.dialCodeMapping[c[2]] = {
39913 priority: c[3] || 0,
39914 areaCodes: c[4] || null
39926 cls : 'form-control tel-input',
39927 autocomplete: 'new-password'
39930 var hiddenInput = {
39933 cls: 'hidden-tel-input'
39937 hiddenInput.name = this.name;
39940 if (this.disabled) {
39941 input.disabled = true;
39944 var flag_container = {
39961 cls: this.hasFeedback ? 'has-feedback' : '',
39967 cls: 'dial-code-holder',
39974 cls: 'roo-select2-container input-group',
39981 if (this.fieldLabel.length) {
39984 tooltip: 'This field is required'
39990 cls: 'control-label',
39996 html: this.fieldLabel
39999 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40005 if(this.indicatorpos == 'right') {
40006 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40013 if(align == 'left') {
40021 if(this.labelWidth > 12){
40022 label.style = "width: " + this.labelWidth + 'px';
40024 if(this.labelWidth < 13 && this.labelmd == 0){
40025 this.labelmd = this.labelWidth;
40027 if(this.labellg > 0){
40028 label.cls += ' col-lg-' + this.labellg;
40029 input.cls += ' col-lg-' + (12 - this.labellg);
40031 if(this.labelmd > 0){
40032 label.cls += ' col-md-' + this.labelmd;
40033 container.cls += ' col-md-' + (12 - this.labelmd);
40035 if(this.labelsm > 0){
40036 label.cls += ' col-sm-' + this.labelsm;
40037 container.cls += ' col-sm-' + (12 - this.labelsm);
40039 if(this.labelxs > 0){
40040 label.cls += ' col-xs-' + this.labelxs;
40041 container.cls += ' col-xs-' + (12 - this.labelxs);
40051 var settings = this;
40053 ['xs','sm','md','lg'].map(function(size){
40054 if (settings[size]) {
40055 cfg.cls += ' col-' + size + '-' + settings[size];
40059 this.store = new Roo.data.Store({
40060 proxy : new Roo.data.MemoryProxy({}),
40061 reader : new Roo.data.JsonReader({
40072 'name' : 'dialCode',
40076 'name' : 'priority',
40080 'name' : 'areaCodes',
40087 if(!this.preferedCountries) {
40088 this.preferedCountries = [
40095 var p = this.preferedCountries.reverse();
40098 for (var i = 0; i < p.length; i++) {
40099 for (var j = 0; j < this.allCountries.length; j++) {
40100 if(this.allCountries[j].iso2 == p[i]) {
40101 var t = this.allCountries[j];
40102 this.allCountries.splice(j,1);
40103 this.allCountries.unshift(t);
40109 this.store.proxy.data = {
40111 data: this.allCountries
40117 initEvents : function()
40120 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40122 this.indicator = this.indicatorEl();
40123 this.flag = this.flagEl();
40124 this.dialCodeHolder = this.dialCodeHolderEl();
40126 this.trigger = this.el.select('div.flag-box',true).first();
40127 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40132 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40133 _this.list.setWidth(lw);
40136 this.list.on('mouseover', this.onViewOver, this);
40137 this.list.on('mousemove', this.onViewMove, this);
40138 this.inputEl().on("keyup", this.onKeyUp, this);
40140 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40142 this.view = new Roo.View(this.list, this.tpl, {
40143 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40146 this.view.on('click', this.onViewClick, this);
40147 this.setValue(this.defaultDialCode);
40150 onTriggerClick : function(e)
40152 Roo.log('trigger click');
40157 if(this.isExpanded()){
40159 this.hasFocus = false;
40161 this.store.load({});
40162 this.hasFocus = true;
40167 isExpanded : function()
40169 return this.list.isVisible();
40172 collapse : function()
40174 if(!this.isExpanded()){
40178 Roo.get(document).un('mousedown', this.collapseIf, this);
40179 Roo.get(document).un('mousewheel', this.collapseIf, this);
40180 this.fireEvent('collapse', this);
40184 expand : function()
40188 if(this.isExpanded() || !this.hasFocus){
40192 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40193 this.list.setWidth(lw);
40196 this.restrictHeight();
40198 Roo.get(document).on('mousedown', this.collapseIf, this);
40199 Roo.get(document).on('mousewheel', this.collapseIf, this);
40201 this.fireEvent('expand', this);
40204 restrictHeight : function()
40206 this.list.alignTo(this.inputEl(), this.listAlign);
40207 this.list.alignTo(this.inputEl(), this.listAlign);
40210 onViewOver : function(e, t)
40212 if(this.inKeyMode){
40215 var item = this.view.findItemFromChild(t);
40218 var index = this.view.indexOf(item);
40219 this.select(index, false);
40224 onViewClick : function(view, doFocus, el, e)
40226 var index = this.view.getSelectedIndexes()[0];
40228 var r = this.store.getAt(index);
40231 this.onSelect(r, index);
40233 if(doFocus !== false && !this.blockFocus){
40234 this.inputEl().focus();
40238 onViewMove : function(e, t)
40240 this.inKeyMode = false;
40243 select : function(index, scrollIntoView)
40245 this.selectedIndex = index;
40246 this.view.select(index);
40247 if(scrollIntoView !== false){
40248 var el = this.view.getNode(index);
40250 this.list.scrollChildIntoView(el, false);
40255 createList : function()
40257 this.list = Roo.get(document.body).createChild({
40259 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40260 style: 'display:none'
40263 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40266 collapseIf : function(e)
40268 var in_combo = e.within(this.el);
40269 var in_list = e.within(this.list);
40270 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40272 if (in_combo || in_list || is_list) {
40278 onSelect : function(record, index)
40280 if(this.fireEvent('beforeselect', this, record, index) !== false){
40282 this.setFlagClass(record.data.iso2);
40283 this.setDialCode(record.data.dialCode);
40284 this.hasFocus = false;
40286 this.fireEvent('select', this, record, index);
40290 flagEl : function()
40292 var flag = this.el.select('div.flag',true).first();
40299 dialCodeHolderEl : function()
40301 var d = this.el.select('input.dial-code-holder',true).first();
40308 setDialCode : function(v)
40310 this.dialCodeHolder.dom.value = '+'+v;
40313 setFlagClass : function(n)
40315 this.flag.dom.className = 'flag '+n;
40318 getValue : function()
40320 var v = this.inputEl().getValue();
40321 if(this.dialCodeHolder) {
40322 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40327 setValue : function(v)
40329 var d = this.getDialCode(v);
40331 //invalid dial code
40332 if(v.length == 0 || !d || d.length == 0) {
40334 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40335 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40341 this.setFlagClass(this.dialCodeMapping[d].iso2);
40342 this.setDialCode(d);
40343 this.inputEl().dom.value = v.replace('+'+d,'');
40344 this.hiddenEl().dom.value = this.getValue();
40349 getDialCode : function(v)
40353 if (v.length == 0) {
40354 return this.dialCodeHolder.dom.value;
40358 if (v.charAt(0) != "+") {
40361 var numericChars = "";
40362 for (var i = 1; i < v.length; i++) {
40363 var c = v.charAt(i);
40366 if (this.dialCodeMapping[numericChars]) {
40367 dialCode = v.substr(1, i);
40369 if (numericChars.length == 4) {
40379 this.setValue(this.defaultDialCode);
40383 hiddenEl : function()
40385 return this.el.select('input.hidden-tel-input',true).first();
40388 onKeyUp : function(e){
40390 var k = e.getKey();
40391 var c = e.getCharCode();
40394 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40395 this.allowed.indexOf(String.fromCharCode(c)) === -1
40400 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40403 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40407 this.setValue(this.getValue());
40412 * @class Roo.bootstrap.MoneyField
40413 * @extends Roo.bootstrap.ComboBox
40414 * Bootstrap MoneyField class
40417 * Create a new MoneyField.
40418 * @param {Object} config Configuration options
40421 Roo.bootstrap.MoneyField = function(config) {
40423 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40427 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40430 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40432 allowDecimals : true,
40434 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40436 decimalSeparator : ".",
40438 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40440 decimalPrecision : 0,
40442 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40444 allowNegative : true,
40446 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40450 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40452 minValue : Number.NEGATIVE_INFINITY,
40454 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40456 maxValue : Number.MAX_VALUE,
40458 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40460 minText : "The minimum value for this field is {0}",
40462 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40464 maxText : "The maximum value for this field is {0}",
40466 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40467 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40469 nanText : "{0} is not a valid number",
40471 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40475 * @cfg {String} defaults currency of the MoneyField
40476 * value should be in lkey
40478 defaultCurrency : false,
40480 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40482 thousandsDelimiter : false,
40492 getAutoCreate : function()
40494 var align = this.labelAlign || this.parentLabelAlign();
40506 cls : 'form-control roo-money-amount-input',
40507 autocomplete: 'new-password'
40510 var hiddenInput = {
40514 cls: 'hidden-number-input'
40518 hiddenInput.name = this.name;
40521 if (this.disabled) {
40522 input.disabled = true;
40525 var clg = 12 - this.inputlg;
40526 var cmd = 12 - this.inputmd;
40527 var csm = 12 - this.inputsm;
40528 var cxs = 12 - this.inputxs;
40532 cls : 'row roo-money-field',
40536 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40540 cls: 'roo-select2-container input-group',
40544 cls : 'form-control roo-money-currency-input',
40545 autocomplete: 'new-password',
40547 name : this.currencyName
40551 cls : 'input-group-addon',
40565 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40569 cls: this.hasFeedback ? 'has-feedback' : '',
40580 if (this.fieldLabel.length) {
40583 tooltip: 'This field is required'
40589 cls: 'control-label',
40595 html: this.fieldLabel
40598 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40604 if(this.indicatorpos == 'right') {
40605 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40612 if(align == 'left') {
40620 if(this.labelWidth > 12){
40621 label.style = "width: " + this.labelWidth + 'px';
40623 if(this.labelWidth < 13 && this.labelmd == 0){
40624 this.labelmd = this.labelWidth;
40626 if(this.labellg > 0){
40627 label.cls += ' col-lg-' + this.labellg;
40628 input.cls += ' col-lg-' + (12 - this.labellg);
40630 if(this.labelmd > 0){
40631 label.cls += ' col-md-' + this.labelmd;
40632 container.cls += ' col-md-' + (12 - this.labelmd);
40634 if(this.labelsm > 0){
40635 label.cls += ' col-sm-' + this.labelsm;
40636 container.cls += ' col-sm-' + (12 - this.labelsm);
40638 if(this.labelxs > 0){
40639 label.cls += ' col-xs-' + this.labelxs;
40640 container.cls += ' col-xs-' + (12 - this.labelxs);
40651 var settings = this;
40653 ['xs','sm','md','lg'].map(function(size){
40654 if (settings[size]) {
40655 cfg.cls += ' col-' + size + '-' + settings[size];
40662 initEvents : function()
40664 this.indicator = this.indicatorEl();
40666 this.initCurrencyEvent();
40668 this.initNumberEvent();
40671 initCurrencyEvent : function()
40674 throw "can not find store for combo";
40677 this.store = Roo.factory(this.store, Roo.data);
40678 this.store.parent = this;
40682 this.triggerEl = this.el.select('.input-group-addon', true).first();
40684 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40689 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40690 _this.list.setWidth(lw);
40693 this.list.on('mouseover', this.onViewOver, this);
40694 this.list.on('mousemove', this.onViewMove, this);
40695 this.list.on('scroll', this.onViewScroll, this);
40698 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40701 this.view = new Roo.View(this.list, this.tpl, {
40702 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40705 this.view.on('click', this.onViewClick, this);
40707 this.store.on('beforeload', this.onBeforeLoad, this);
40708 this.store.on('load', this.onLoad, this);
40709 this.store.on('loadexception', this.onLoadException, this);
40711 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40712 "up" : function(e){
40713 this.inKeyMode = true;
40717 "down" : function(e){
40718 if(!this.isExpanded()){
40719 this.onTriggerClick();
40721 this.inKeyMode = true;
40726 "enter" : function(e){
40729 if(this.fireEvent("specialkey", this, e)){
40730 this.onViewClick(false);
40736 "esc" : function(e){
40740 "tab" : function(e){
40743 if(this.fireEvent("specialkey", this, e)){
40744 this.onViewClick(false);
40752 doRelay : function(foo, bar, hname){
40753 if(hname == 'down' || this.scope.isExpanded()){
40754 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40762 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40766 initNumberEvent : function(e)
40768 this.inputEl().on("keydown" , this.fireKey, this);
40769 this.inputEl().on("focus", this.onFocus, this);
40770 this.inputEl().on("blur", this.onBlur, this);
40772 this.inputEl().relayEvent('keyup', this);
40774 if(this.indicator){
40775 this.indicator.addClass('invisible');
40778 this.originalValue = this.getValue();
40780 if(this.validationEvent == 'keyup'){
40781 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40782 this.inputEl().on('keyup', this.filterValidation, this);
40784 else if(this.validationEvent !== false){
40785 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40788 if(this.selectOnFocus){
40789 this.on("focus", this.preFocus, this);
40792 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40793 this.inputEl().on("keypress", this.filterKeys, this);
40795 this.inputEl().relayEvent('keypress', this);
40798 var allowed = "0123456789";
40800 if(this.allowDecimals){
40801 allowed += this.decimalSeparator;
40804 if(this.allowNegative){
40808 if(this.thousandsDelimiter) {
40812 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40814 var keyPress = function(e){
40816 var k = e.getKey();
40818 var c = e.getCharCode();
40821 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40822 allowed.indexOf(String.fromCharCode(c)) === -1
40828 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40832 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40837 this.inputEl().on("keypress", keyPress, this);
40841 onTriggerClick : function(e)
40848 this.loadNext = false;
40850 if(this.isExpanded()){
40855 this.hasFocus = true;
40857 if(this.triggerAction == 'all') {
40858 this.doQuery(this.allQuery, true);
40862 this.doQuery(this.getRawValue());
40865 getCurrency : function()
40867 var v = this.currencyEl().getValue();
40872 restrictHeight : function()
40874 this.list.alignTo(this.currencyEl(), this.listAlign);
40875 this.list.alignTo(this.currencyEl(), this.listAlign);
40878 onViewClick : function(view, doFocus, el, e)
40880 var index = this.view.getSelectedIndexes()[0];
40882 var r = this.store.getAt(index);
40885 this.onSelect(r, index);
40889 onSelect : function(record, index){
40891 if(this.fireEvent('beforeselect', this, record, index) !== false){
40893 this.setFromCurrencyData(index > -1 ? record.data : false);
40897 this.fireEvent('select', this, record, index);
40901 setFromCurrencyData : function(o)
40905 this.lastCurrency = o;
40907 if (this.currencyField) {
40908 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40910 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40913 this.lastSelectionText = currency;
40915 //setting default currency
40916 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40917 this.setCurrency(this.defaultCurrency);
40921 this.setCurrency(currency);
40924 setFromData : function(o)
40928 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40930 this.setFromCurrencyData(c);
40935 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40937 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40940 this.setValue(value);
40944 setCurrency : function(v)
40946 this.currencyValue = v;
40949 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40954 setValue : function(v)
40956 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40962 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40964 this.inputEl().dom.value = (v == '') ? '' :
40965 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40967 if(!this.allowZero && v === '0') {
40968 this.hiddenEl().dom.value = '';
40969 this.inputEl().dom.value = '';
40976 getRawValue : function()
40978 var v = this.inputEl().getValue();
40983 getValue : function()
40985 return this.fixPrecision(this.parseValue(this.getRawValue()));
40988 parseValue : function(value)
40990 if(this.thousandsDelimiter) {
40992 r = new RegExp(",", "g");
40993 value = value.replace(r, "");
40996 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40997 return isNaN(value) ? '' : value;
41001 fixPrecision : function(value)
41003 if(this.thousandsDelimiter) {
41005 r = new RegExp(",", "g");
41006 value = value.replace(r, "");
41009 var nan = isNaN(value);
41011 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41012 return nan ? '' : value;
41014 return parseFloat(value).toFixed(this.decimalPrecision);
41017 decimalPrecisionFcn : function(v)
41019 return Math.floor(v);
41022 validateValue : function(value)
41024 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41028 var num = this.parseValue(value);
41031 this.markInvalid(String.format(this.nanText, value));
41035 if(num < this.minValue){
41036 this.markInvalid(String.format(this.minText, this.minValue));
41040 if(num > this.maxValue){
41041 this.markInvalid(String.format(this.maxText, this.maxValue));
41048 validate : function()
41050 if(this.disabled || this.allowBlank){
41055 var currency = this.getCurrency();
41057 if(this.validateValue(this.getRawValue()) && currency.length){
41062 this.markInvalid();
41066 getName: function()
41071 beforeBlur : function()
41077 var v = this.parseValue(this.getRawValue());
41084 onBlur : function()
41088 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41089 //this.el.removeClass(this.focusClass);
41092 this.hasFocus = false;
41094 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41098 var v = this.getValue();
41100 if(String(v) !== String(this.startValue)){
41101 this.fireEvent('change', this, v, this.startValue);
41104 this.fireEvent("blur", this);
41107 inputEl : function()
41109 return this.el.select('.roo-money-amount-input', true).first();
41112 currencyEl : function()
41114 return this.el.select('.roo-money-currency-input', true).first();
41117 hiddenEl : function()
41119 return this.el.select('input.hidden-number-input',true).first();