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){
18950 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18953 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18958 if(this.isInline) {
18962 this.picker().removeClass(['bottom', 'top']);
18964 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18966 * place to the top of element!
18970 this.picker().addClass('top');
18971 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18976 this.picker().addClass('bottom');
18978 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18981 parseDate : function(value)
18983 if(!value || value instanceof Date){
18986 var v = Date.parseDate(value, this.format);
18987 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18988 v = Date.parseDate(value, 'Y-m-d');
18990 if(!v && this.altFormats){
18991 if(!this.altFormatsArray){
18992 this.altFormatsArray = this.altFormats.split("|");
18994 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18995 v = Date.parseDate(value, this.altFormatsArray[i]);
19001 formatDate : function(date, fmt)
19003 return (!date || !(date instanceof Date)) ?
19004 date : date.dateFormat(fmt || this.format);
19007 onFocus : function()
19009 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19013 onBlur : function()
19015 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19017 var d = this.inputEl().getValue();
19024 showPopup : function()
19026 this.picker().show();
19030 this.fireEvent('showpopup', this, this.date);
19033 hidePopup : function()
19035 if(this.isInline) {
19038 this.picker().hide();
19039 this.viewMode = this.startViewMode;
19042 this.fireEvent('hidepopup', this, this.date);
19046 onMousedown: function(e)
19048 e.stopPropagation();
19049 e.preventDefault();
19054 Roo.bootstrap.DateField.superclass.keyup.call(this);
19058 setValue: function(v)
19060 if(this.fireEvent('beforeselect', this, v) !== false){
19061 var d = new Date(this.parseDate(v) ).clearTime();
19063 if(isNaN(d.getTime())){
19064 this.date = this.viewDate = '';
19065 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19069 v = this.formatDate(d);
19071 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19073 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19077 this.fireEvent('select', this, this.date);
19081 getValue: function()
19083 return this.formatDate(this.date);
19086 fireKey: function(e)
19088 if (!this.picker().isVisible()){
19089 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19095 var dateChanged = false,
19097 newDate, newViewDate;
19102 e.preventDefault();
19106 if (!this.keyboardNavigation) {
19109 dir = e.keyCode == 37 ? -1 : 1;
19112 newDate = this.moveYear(this.date, dir);
19113 newViewDate = this.moveYear(this.viewDate, dir);
19114 } else if (e.shiftKey){
19115 newDate = this.moveMonth(this.date, dir);
19116 newViewDate = this.moveMonth(this.viewDate, dir);
19118 newDate = new Date(this.date);
19119 newDate.setUTCDate(this.date.getUTCDate() + dir);
19120 newViewDate = new Date(this.viewDate);
19121 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19123 if (this.dateWithinRange(newDate)){
19124 this.date = newDate;
19125 this.viewDate = newViewDate;
19126 this.setValue(this.formatDate(this.date));
19128 e.preventDefault();
19129 dateChanged = true;
19134 if (!this.keyboardNavigation) {
19137 dir = e.keyCode == 38 ? -1 : 1;
19139 newDate = this.moveYear(this.date, dir);
19140 newViewDate = this.moveYear(this.viewDate, dir);
19141 } else if (e.shiftKey){
19142 newDate = this.moveMonth(this.date, dir);
19143 newViewDate = this.moveMonth(this.viewDate, dir);
19145 newDate = new Date(this.date);
19146 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19147 newViewDate = new Date(this.viewDate);
19148 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19150 if (this.dateWithinRange(newDate)){
19151 this.date = newDate;
19152 this.viewDate = newViewDate;
19153 this.setValue(this.formatDate(this.date));
19155 e.preventDefault();
19156 dateChanged = true;
19160 this.setValue(this.formatDate(this.date));
19162 e.preventDefault();
19165 this.setValue(this.formatDate(this.date));
19179 onClick: function(e)
19181 e.stopPropagation();
19182 e.preventDefault();
19184 var target = e.getTarget();
19186 if(target.nodeName.toLowerCase() === 'i'){
19187 target = Roo.get(target).dom.parentNode;
19190 var nodeName = target.nodeName;
19191 var className = target.className;
19192 var html = target.innerHTML;
19193 //Roo.log(nodeName);
19195 switch(nodeName.toLowerCase()) {
19197 switch(className) {
19203 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19204 switch(this.viewMode){
19206 this.viewDate = this.moveMonth(this.viewDate, dir);
19210 this.viewDate = this.moveYear(this.viewDate, dir);
19216 var date = new Date();
19217 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19219 this.setValue(this.formatDate(this.date));
19226 if (className.indexOf('disabled') < 0) {
19227 this.viewDate.setUTCDate(1);
19228 if (className.indexOf('month') > -1) {
19229 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19231 var year = parseInt(html, 10) || 0;
19232 this.viewDate.setUTCFullYear(year);
19236 if(this.singleMode){
19237 this.setValue(this.formatDate(this.viewDate));
19248 //Roo.log(className);
19249 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19250 var day = parseInt(html, 10) || 1;
19251 var year = this.viewDate.getUTCFullYear(),
19252 month = this.viewDate.getUTCMonth();
19254 if (className.indexOf('old') > -1) {
19261 } else if (className.indexOf('new') > -1) {
19269 //Roo.log([year,month,day]);
19270 this.date = this.UTCDate(year, month, day,0,0,0,0);
19271 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19273 //Roo.log(this.formatDate(this.date));
19274 this.setValue(this.formatDate(this.date));
19281 setStartDate: function(startDate)
19283 this.startDate = startDate || -Infinity;
19284 if (this.startDate !== -Infinity) {
19285 this.startDate = this.parseDate(this.startDate);
19288 this.updateNavArrows();
19291 setEndDate: function(endDate)
19293 this.endDate = endDate || Infinity;
19294 if (this.endDate !== Infinity) {
19295 this.endDate = this.parseDate(this.endDate);
19298 this.updateNavArrows();
19301 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19303 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19304 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19305 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19307 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19308 return parseInt(d, 10);
19311 this.updateNavArrows();
19314 updateNavArrows: function()
19316 if(this.singleMode){
19320 var d = new Date(this.viewDate),
19321 year = d.getUTCFullYear(),
19322 month = d.getUTCMonth();
19324 Roo.each(this.picker().select('.prev', true).elements, function(v){
19326 switch (this.viewMode) {
19329 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19335 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19342 Roo.each(this.picker().select('.next', true).elements, function(v){
19344 switch (this.viewMode) {
19347 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19353 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19361 moveMonth: function(date, dir)
19366 var new_date = new Date(date.valueOf()),
19367 day = new_date.getUTCDate(),
19368 month = new_date.getUTCMonth(),
19369 mag = Math.abs(dir),
19371 dir = dir > 0 ? 1 : -1;
19374 // If going back one month, make sure month is not current month
19375 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19377 return new_date.getUTCMonth() == month;
19379 // If going forward one month, make sure month is as expected
19380 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19382 return new_date.getUTCMonth() != new_month;
19384 new_month = month + dir;
19385 new_date.setUTCMonth(new_month);
19386 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19387 if (new_month < 0 || new_month > 11) {
19388 new_month = (new_month + 12) % 12;
19391 // For magnitudes >1, move one month at a time...
19392 for (var i=0; i<mag; i++) {
19393 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19394 new_date = this.moveMonth(new_date, dir);
19396 // ...then reset the day, keeping it in the new month
19397 new_month = new_date.getUTCMonth();
19398 new_date.setUTCDate(day);
19400 return new_month != new_date.getUTCMonth();
19403 // Common date-resetting loop -- if date is beyond end of month, make it
19406 new_date.setUTCDate(--day);
19407 new_date.setUTCMonth(new_month);
19412 moveYear: function(date, dir)
19414 return this.moveMonth(date, dir*12);
19417 dateWithinRange: function(date)
19419 return date >= this.startDate && date <= this.endDate;
19425 this.picker().remove();
19428 validateValue : function(value)
19430 if(this.getVisibilityEl().hasClass('hidden')){
19434 if(value.length < 1) {
19435 if(this.allowBlank){
19441 if(value.length < this.minLength){
19444 if(value.length > this.maxLength){
19448 var vt = Roo.form.VTypes;
19449 if(!vt[this.vtype](value, this)){
19453 if(typeof this.validator == "function"){
19454 var msg = this.validator(value);
19460 if(this.regex && !this.regex.test(value)){
19464 if(typeof(this.parseDate(value)) == 'undefined'){
19468 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19472 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19482 this.date = this.viewDate = '';
19484 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19489 Roo.apply(Roo.bootstrap.DateField, {
19500 html: '<i class="fa fa-arrow-left"/>'
19510 html: '<i class="fa fa-arrow-right"/>'
19552 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19553 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19554 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19555 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19556 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19569 navFnc: 'FullYear',
19574 navFnc: 'FullYear',
19579 Roo.apply(Roo.bootstrap.DateField, {
19583 cls: 'datepicker dropdown-menu roo-dynamic',
19587 cls: 'datepicker-days',
19591 cls: 'table-condensed',
19593 Roo.bootstrap.DateField.head,
19597 Roo.bootstrap.DateField.footer
19604 cls: 'datepicker-months',
19608 cls: 'table-condensed',
19610 Roo.bootstrap.DateField.head,
19611 Roo.bootstrap.DateField.content,
19612 Roo.bootstrap.DateField.footer
19619 cls: 'datepicker-years',
19623 cls: 'table-condensed',
19625 Roo.bootstrap.DateField.head,
19626 Roo.bootstrap.DateField.content,
19627 Roo.bootstrap.DateField.footer
19646 * @class Roo.bootstrap.TimeField
19647 * @extends Roo.bootstrap.Input
19648 * Bootstrap DateField class
19652 * Create a new TimeField
19653 * @param {Object} config The config object
19656 Roo.bootstrap.TimeField = function(config){
19657 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19661 * Fires when this field show.
19662 * @param {Roo.bootstrap.DateField} thisthis
19663 * @param {Mixed} date The date value
19668 * Fires when this field hide.
19669 * @param {Roo.bootstrap.DateField} this
19670 * @param {Mixed} date The date value
19675 * Fires when select a date.
19676 * @param {Roo.bootstrap.DateField} this
19677 * @param {Mixed} date The date value
19683 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19686 * @cfg {String} format
19687 * The default time format string which can be overriden for localization support. The format must be
19688 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19692 onRender: function(ct, position)
19695 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19697 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19699 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19701 this.pop = this.picker().select('>.datepicker-time',true).first();
19702 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19704 this.picker().on('mousedown', this.onMousedown, this);
19705 this.picker().on('click', this.onClick, this);
19707 this.picker().addClass('datepicker-dropdown');
19712 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19713 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19714 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19715 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19716 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19717 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19721 fireKey: function(e){
19722 if (!this.picker().isVisible()){
19723 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19729 e.preventDefault();
19737 this.onTogglePeriod();
19740 this.onIncrementMinutes();
19743 this.onDecrementMinutes();
19752 onClick: function(e) {
19753 e.stopPropagation();
19754 e.preventDefault();
19757 picker : function()
19759 return this.el.select('.datepicker', true).first();
19762 fillTime: function()
19764 var time = this.pop.select('tbody', true).first();
19766 time.dom.innerHTML = '';
19781 cls: 'hours-up glyphicon glyphicon-chevron-up'
19801 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19822 cls: 'timepicker-hour',
19837 cls: 'timepicker-minute',
19852 cls: 'btn btn-primary period',
19874 cls: 'hours-down glyphicon glyphicon-chevron-down'
19894 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19912 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19919 var hours = this.time.getHours();
19920 var minutes = this.time.getMinutes();
19933 hours = hours - 12;
19937 hours = '0' + hours;
19941 minutes = '0' + minutes;
19944 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19945 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19946 this.pop.select('button', true).first().dom.innerHTML = period;
19952 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19954 var cls = ['bottom'];
19956 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19963 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19968 this.picker().addClass(cls.join('-'));
19972 Roo.each(cls, function(c){
19974 _this.picker().setTop(_this.inputEl().getHeight());
19978 _this.picker().setTop(0 - _this.picker().getHeight());
19983 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19987 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19994 onFocus : function()
19996 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20000 onBlur : function()
20002 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20008 this.picker().show();
20013 this.fireEvent('show', this, this.date);
20018 this.picker().hide();
20021 this.fireEvent('hide', this, this.date);
20024 setTime : function()
20027 this.setValue(this.time.format(this.format));
20029 this.fireEvent('select', this, this.date);
20034 onMousedown: function(e){
20035 e.stopPropagation();
20036 e.preventDefault();
20039 onIncrementHours: function()
20041 Roo.log('onIncrementHours');
20042 this.time = this.time.add(Date.HOUR, 1);
20047 onDecrementHours: function()
20049 Roo.log('onDecrementHours');
20050 this.time = this.time.add(Date.HOUR, -1);
20054 onIncrementMinutes: function()
20056 Roo.log('onIncrementMinutes');
20057 this.time = this.time.add(Date.MINUTE, 1);
20061 onDecrementMinutes: function()
20063 Roo.log('onDecrementMinutes');
20064 this.time = this.time.add(Date.MINUTE, -1);
20068 onTogglePeriod: function()
20070 Roo.log('onTogglePeriod');
20071 this.time = this.time.add(Date.HOUR, 12);
20078 Roo.apply(Roo.bootstrap.TimeField, {
20108 cls: 'btn btn-info ok',
20120 Roo.apply(Roo.bootstrap.TimeField, {
20124 cls: 'datepicker dropdown-menu',
20128 cls: 'datepicker-time',
20132 cls: 'table-condensed',
20134 Roo.bootstrap.TimeField.content,
20135 Roo.bootstrap.TimeField.footer
20154 * @class Roo.bootstrap.MonthField
20155 * @extends Roo.bootstrap.Input
20156 * Bootstrap MonthField class
20158 * @cfg {String} language default en
20161 * Create a new MonthField
20162 * @param {Object} config The config object
20165 Roo.bootstrap.MonthField = function(config){
20166 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20171 * Fires when this field show.
20172 * @param {Roo.bootstrap.MonthField} this
20173 * @param {Mixed} date The date value
20178 * Fires when this field hide.
20179 * @param {Roo.bootstrap.MonthField} this
20180 * @param {Mixed} date The date value
20185 * Fires when select a date.
20186 * @param {Roo.bootstrap.MonthField} this
20187 * @param {String} oldvalue The old value
20188 * @param {String} newvalue The new value
20194 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20196 onRender: function(ct, position)
20199 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20201 this.language = this.language || 'en';
20202 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20203 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20205 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20206 this.isInline = false;
20207 this.isInput = true;
20208 this.component = this.el.select('.add-on', true).first() || false;
20209 this.component = (this.component && this.component.length === 0) ? false : this.component;
20210 this.hasInput = this.component && this.inputEL().length;
20212 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20214 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20216 this.picker().on('mousedown', this.onMousedown, this);
20217 this.picker().on('click', this.onClick, this);
20219 this.picker().addClass('datepicker-dropdown');
20221 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20222 v.setStyle('width', '189px');
20229 if(this.isInline) {
20235 setValue: function(v, suppressEvent)
20237 var o = this.getValue();
20239 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20243 if(suppressEvent !== true){
20244 this.fireEvent('select', this, o, v);
20249 getValue: function()
20254 onClick: function(e)
20256 e.stopPropagation();
20257 e.preventDefault();
20259 var target = e.getTarget();
20261 if(target.nodeName.toLowerCase() === 'i'){
20262 target = Roo.get(target).dom.parentNode;
20265 var nodeName = target.nodeName;
20266 var className = target.className;
20267 var html = target.innerHTML;
20269 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20273 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20275 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20281 picker : function()
20283 return this.pickerEl;
20286 fillMonths: function()
20289 var months = this.picker().select('>.datepicker-months td', true).first();
20291 months.dom.innerHTML = '';
20297 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20300 months.createChild(month);
20309 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20310 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20313 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20314 e.removeClass('active');
20316 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20317 e.addClass('active');
20324 if(this.isInline) {
20328 this.picker().removeClass(['bottom', 'top']);
20330 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20332 * place to the top of element!
20336 this.picker().addClass('top');
20337 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20342 this.picker().addClass('bottom');
20344 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20347 onFocus : function()
20349 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20353 onBlur : function()
20355 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20357 var d = this.inputEl().getValue();
20366 this.picker().show();
20367 this.picker().select('>.datepicker-months', true).first().show();
20371 this.fireEvent('show', this, this.date);
20376 if(this.isInline) {
20379 this.picker().hide();
20380 this.fireEvent('hide', this, this.date);
20384 onMousedown: function(e)
20386 e.stopPropagation();
20387 e.preventDefault();
20392 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20396 fireKey: function(e)
20398 if (!this.picker().isVisible()){
20399 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20410 e.preventDefault();
20414 dir = e.keyCode == 37 ? -1 : 1;
20416 this.vIndex = this.vIndex + dir;
20418 if(this.vIndex < 0){
20422 if(this.vIndex > 11){
20426 if(isNaN(this.vIndex)){
20430 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20436 dir = e.keyCode == 38 ? -1 : 1;
20438 this.vIndex = this.vIndex + dir * 4;
20440 if(this.vIndex < 0){
20444 if(this.vIndex > 11){
20448 if(isNaN(this.vIndex)){
20452 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20457 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20458 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20462 e.preventDefault();
20465 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20466 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20482 this.picker().remove();
20487 Roo.apply(Roo.bootstrap.MonthField, {
20506 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20507 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20512 Roo.apply(Roo.bootstrap.MonthField, {
20516 cls: 'datepicker dropdown-menu roo-dynamic',
20520 cls: 'datepicker-months',
20524 cls: 'table-condensed',
20526 Roo.bootstrap.DateField.content
20546 * @class Roo.bootstrap.CheckBox
20547 * @extends Roo.bootstrap.Input
20548 * Bootstrap CheckBox class
20550 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20551 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20552 * @cfg {String} boxLabel The text that appears beside the checkbox
20553 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20554 * @cfg {Boolean} checked initnal the element
20555 * @cfg {Boolean} inline inline the element (default false)
20556 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20557 * @cfg {String} tooltip label tooltip
20560 * Create a new CheckBox
20561 * @param {Object} config The config object
20564 Roo.bootstrap.CheckBox = function(config){
20565 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20570 * Fires when the element is checked or unchecked.
20571 * @param {Roo.bootstrap.CheckBox} this This input
20572 * @param {Boolean} checked The new checked value
20577 * Fires when the element is click.
20578 * @param {Roo.bootstrap.CheckBox} this This input
20585 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20587 inputType: 'checkbox',
20596 getAutoCreate : function()
20598 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20604 cfg.cls = 'form-group ' + this.inputType; //input-group
20607 cfg.cls += ' ' + this.inputType + '-inline';
20613 type : this.inputType,
20614 value : this.inputValue,
20615 cls : 'roo-' + this.inputType, //'form-box',
20616 placeholder : this.placeholder || ''
20620 if(this.inputType != 'radio'){
20624 cls : 'roo-hidden-value',
20625 value : this.checked ? this.inputValue : this.valueOff
20630 if (this.weight) { // Validity check?
20631 cfg.cls += " " + this.inputType + "-" + this.weight;
20634 if (this.disabled) {
20635 input.disabled=true;
20639 input.checked = this.checked;
20644 input.name = this.name;
20646 if(this.inputType != 'radio'){
20647 hidden.name = this.name;
20648 input.name = '_hidden_' + this.name;
20653 input.cls += ' input-' + this.size;
20658 ['xs','sm','md','lg'].map(function(size){
20659 if (settings[size]) {
20660 cfg.cls += ' col-' + size + '-' + settings[size];
20664 var inputblock = input;
20666 if (this.before || this.after) {
20669 cls : 'input-group',
20674 inputblock.cn.push({
20676 cls : 'input-group-addon',
20681 inputblock.cn.push(input);
20683 if(this.inputType != 'radio'){
20684 inputblock.cn.push(hidden);
20688 inputblock.cn.push({
20690 cls : 'input-group-addon',
20697 if (align ==='left' && this.fieldLabel.length) {
20698 // Roo.log("left and has label");
20703 cls : 'control-label',
20704 html : this.fieldLabel
20714 if(this.labelWidth > 12){
20715 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20718 if(this.labelWidth < 13 && this.labelmd == 0){
20719 this.labelmd = this.labelWidth;
20722 if(this.labellg > 0){
20723 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20724 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20727 if(this.labelmd > 0){
20728 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20729 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20732 if(this.labelsm > 0){
20733 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20734 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20737 if(this.labelxs > 0){
20738 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20739 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20742 } else if ( this.fieldLabel.length) {
20743 // Roo.log(" label");
20747 tag: this.boxLabel ? 'span' : 'label',
20749 cls: 'control-label box-input-label',
20750 //cls : 'input-group-addon',
20751 html : this.fieldLabel
20760 // Roo.log(" no label && no align");
20761 cfg.cn = [ inputblock ] ;
20767 var boxLabelCfg = {
20769 //'for': id, // box label is handled by onclick - so no for...
20771 html: this.boxLabel
20775 boxLabelCfg.tooltip = this.tooltip;
20778 cfg.cn.push(boxLabelCfg);
20781 if(this.inputType != 'radio'){
20782 cfg.cn.push(hidden);
20790 * return the real input element.
20792 inputEl: function ()
20794 return this.el.select('input.roo-' + this.inputType,true).first();
20796 hiddenEl: function ()
20798 return this.el.select('input.roo-hidden-value',true).first();
20801 labelEl: function()
20803 return this.el.select('label.control-label',true).first();
20805 /* depricated... */
20809 return this.labelEl();
20812 boxLabelEl: function()
20814 return this.el.select('label.box-label',true).first();
20817 initEvents : function()
20819 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20821 this.inputEl().on('click', this.onClick, this);
20823 if (this.boxLabel) {
20824 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20827 this.startValue = this.getValue();
20830 Roo.bootstrap.CheckBox.register(this);
20834 onClick : function(e)
20836 if(this.fireEvent('click', this, e) !== false){
20837 this.setChecked(!this.checked);
20842 setChecked : function(state,suppressEvent)
20844 this.startValue = this.getValue();
20846 if(this.inputType == 'radio'){
20848 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20849 e.dom.checked = false;
20852 this.inputEl().dom.checked = true;
20854 this.inputEl().dom.value = this.inputValue;
20856 if(suppressEvent !== true){
20857 this.fireEvent('check', this, true);
20865 this.checked = state;
20867 this.inputEl().dom.checked = state;
20870 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20872 if(suppressEvent !== true){
20873 this.fireEvent('check', this, state);
20879 getValue : function()
20881 if(this.inputType == 'radio'){
20882 return this.getGroupValue();
20885 return this.hiddenEl().dom.value;
20889 getGroupValue : function()
20891 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20895 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20898 setValue : function(v,suppressEvent)
20900 if(this.inputType == 'radio'){
20901 this.setGroupValue(v, suppressEvent);
20905 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20910 setGroupValue : function(v, suppressEvent)
20912 this.startValue = this.getValue();
20914 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20915 e.dom.checked = false;
20917 if(e.dom.value == v){
20918 e.dom.checked = true;
20922 if(suppressEvent !== true){
20923 this.fireEvent('check', this, true);
20931 validate : function()
20933 if(this.getVisibilityEl().hasClass('hidden')){
20939 (this.inputType == 'radio' && this.validateRadio()) ||
20940 (this.inputType == 'checkbox' && this.validateCheckbox())
20946 this.markInvalid();
20950 validateRadio : function()
20952 if(this.getVisibilityEl().hasClass('hidden')){
20956 if(this.allowBlank){
20962 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20963 if(!e.dom.checked){
20975 validateCheckbox : function()
20978 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20979 //return (this.getValue() == this.inputValue) ? true : false;
20982 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20990 for(var i in group){
20991 if(group[i].el.isVisible(true)){
20999 for(var i in group){
21004 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21011 * Mark this field as valid
21013 markValid : function()
21017 this.fireEvent('valid', this);
21019 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21022 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21029 if(this.inputType == 'radio'){
21030 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21031 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21032 e.findParent('.form-group', false, true).addClass(_this.validClass);
21039 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21040 this.el.findParent('.form-group', false, true).addClass(this.validClass);
21044 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21050 for(var i in group){
21051 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21052 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21057 * Mark this field as invalid
21058 * @param {String} msg The validation message
21060 markInvalid : function(msg)
21062 if(this.allowBlank){
21068 this.fireEvent('invalid', this, msg);
21070 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21073 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21077 label.markInvalid();
21080 if(this.inputType == 'radio'){
21081 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21082 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21083 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21090 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21091 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21095 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21101 for(var i in group){
21102 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21103 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21108 clearInvalid : function()
21110 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21112 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21114 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21116 if (label && label.iconEl) {
21117 label.iconEl.removeClass(label.validClass);
21118 label.iconEl.removeClass(label.invalidClass);
21122 disable : function()
21124 if(this.inputType != 'radio'){
21125 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21132 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21133 _this.getActionEl().addClass(this.disabledClass);
21134 e.dom.disabled = true;
21138 this.disabled = true;
21139 this.fireEvent("disable", this);
21143 enable : function()
21145 if(this.inputType != 'radio'){
21146 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21153 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21154 _this.getActionEl().removeClass(this.disabledClass);
21155 e.dom.disabled = false;
21159 this.disabled = false;
21160 this.fireEvent("enable", this);
21164 setBoxLabel : function(v)
21169 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21175 Roo.apply(Roo.bootstrap.CheckBox, {
21180 * register a CheckBox Group
21181 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21183 register : function(checkbox)
21185 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21186 this.groups[checkbox.groupId] = {};
21189 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21193 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21197 * fetch a CheckBox Group based on the group ID
21198 * @param {string} the group ID
21199 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21201 get: function(groupId) {
21202 if (typeof(this.groups[groupId]) == 'undefined') {
21206 return this.groups[groupId] ;
21219 * @class Roo.bootstrap.Radio
21220 * @extends Roo.bootstrap.Component
21221 * Bootstrap Radio class
21222 * @cfg {String} boxLabel - the label associated
21223 * @cfg {String} value - the value of radio
21226 * Create a new Radio
21227 * @param {Object} config The config object
21229 Roo.bootstrap.Radio = function(config){
21230 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21234 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21240 getAutoCreate : function()
21244 cls : 'form-group radio',
21249 html : this.boxLabel
21257 initEvents : function()
21259 this.parent().register(this);
21261 this.el.on('click', this.onClick, this);
21265 onClick : function(e)
21267 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21268 this.setChecked(true);
21272 setChecked : function(state, suppressEvent)
21274 this.parent().setValue(this.value, suppressEvent);
21278 setBoxLabel : function(v)
21283 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21298 * @class Roo.bootstrap.SecurePass
21299 * @extends Roo.bootstrap.Input
21300 * Bootstrap SecurePass class
21304 * Create a new SecurePass
21305 * @param {Object} config The config object
21308 Roo.bootstrap.SecurePass = function (config) {
21309 // these go here, so the translation tool can replace them..
21311 PwdEmpty: "Please type a password, and then retype it to confirm.",
21312 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21313 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21314 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21315 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21316 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21317 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21318 TooWeak: "Your password is Too Weak."
21320 this.meterLabel = "Password strength:";
21321 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21322 this.meterClass = [
21323 "roo-password-meter-tooweak",
21324 "roo-password-meter-weak",
21325 "roo-password-meter-medium",
21326 "roo-password-meter-strong",
21327 "roo-password-meter-grey"
21332 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21335 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21337 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21339 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21340 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21341 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21342 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21343 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21344 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21345 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21355 * @cfg {String/Object} Label for the strength meter (defaults to
21356 * 'Password strength:')
21361 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21362 * ['Weak', 'Medium', 'Strong'])
21365 pwdStrengths: false,
21378 initEvents: function ()
21380 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21382 if (this.el.is('input[type=password]') && Roo.isSafari) {
21383 this.el.on('keydown', this.SafariOnKeyDown, this);
21386 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21389 onRender: function (ct, position)
21391 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21392 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21393 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21395 this.trigger.createChild({
21400 cls: 'roo-password-meter-grey col-xs-12',
21403 //width: this.meterWidth + 'px'
21407 cls: 'roo-password-meter-text'
21413 if (this.hideTrigger) {
21414 this.trigger.setDisplayed(false);
21416 this.setSize(this.width || '', this.height || '');
21419 onDestroy: function ()
21421 if (this.trigger) {
21422 this.trigger.removeAllListeners();
21423 this.trigger.remove();
21426 this.wrap.remove();
21428 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21431 checkStrength: function ()
21433 var pwd = this.inputEl().getValue();
21434 if (pwd == this._lastPwd) {
21439 if (this.ClientSideStrongPassword(pwd)) {
21441 } else if (this.ClientSideMediumPassword(pwd)) {
21443 } else if (this.ClientSideWeakPassword(pwd)) {
21449 Roo.log('strength1: ' + strength);
21451 //var pm = this.trigger.child('div/div/div').dom;
21452 var pm = this.trigger.child('div/div');
21453 pm.removeClass(this.meterClass);
21454 pm.addClass(this.meterClass[strength]);
21457 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21459 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21461 this._lastPwd = pwd;
21465 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21467 this._lastPwd = '';
21469 var pm = this.trigger.child('div/div');
21470 pm.removeClass(this.meterClass);
21471 pm.addClass('roo-password-meter-grey');
21474 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21477 this.inputEl().dom.type='password';
21480 validateValue: function (value)
21483 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21486 if (value.length == 0) {
21487 if (this.allowBlank) {
21488 this.clearInvalid();
21492 this.markInvalid(this.errors.PwdEmpty);
21493 this.errorMsg = this.errors.PwdEmpty;
21501 if ('[\x21-\x7e]*'.match(value)) {
21502 this.markInvalid(this.errors.PwdBadChar);
21503 this.errorMsg = this.errors.PwdBadChar;
21506 if (value.length < 6) {
21507 this.markInvalid(this.errors.PwdShort);
21508 this.errorMsg = this.errors.PwdShort;
21511 if (value.length > 16) {
21512 this.markInvalid(this.errors.PwdLong);
21513 this.errorMsg = this.errors.PwdLong;
21517 if (this.ClientSideStrongPassword(value)) {
21519 } else if (this.ClientSideMediumPassword(value)) {
21521 } else if (this.ClientSideWeakPassword(value)) {
21528 if (strength < 2) {
21529 //this.markInvalid(this.errors.TooWeak);
21530 this.errorMsg = this.errors.TooWeak;
21535 console.log('strength2: ' + strength);
21537 //var pm = this.trigger.child('div/div/div').dom;
21539 var pm = this.trigger.child('div/div');
21540 pm.removeClass(this.meterClass);
21541 pm.addClass(this.meterClass[strength]);
21543 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21545 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21547 this.errorMsg = '';
21551 CharacterSetChecks: function (type)
21554 this.fResult = false;
21557 isctype: function (character, type)
21560 case this.kCapitalLetter:
21561 if (character >= 'A' && character <= 'Z') {
21566 case this.kSmallLetter:
21567 if (character >= 'a' && character <= 'z') {
21573 if (character >= '0' && character <= '9') {
21578 case this.kPunctuation:
21579 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21590 IsLongEnough: function (pwd, size)
21592 return !(pwd == null || isNaN(size) || pwd.length < size);
21595 SpansEnoughCharacterSets: function (word, nb)
21597 if (!this.IsLongEnough(word, nb))
21602 var characterSetChecks = new Array(
21603 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21604 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21607 for (var index = 0; index < word.length; ++index) {
21608 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21609 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21610 characterSetChecks[nCharSet].fResult = true;
21617 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21618 if (characterSetChecks[nCharSet].fResult) {
21623 if (nCharSets < nb) {
21629 ClientSideStrongPassword: function (pwd)
21631 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21634 ClientSideMediumPassword: function (pwd)
21636 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21639 ClientSideWeakPassword: function (pwd)
21641 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21644 })//<script type="text/javascript">
21647 * Based Ext JS Library 1.1.1
21648 * Copyright(c) 2006-2007, Ext JS, LLC.
21654 * @class Roo.HtmlEditorCore
21655 * @extends Roo.Component
21656 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21658 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21661 Roo.HtmlEditorCore = function(config){
21664 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21669 * @event initialize
21670 * Fires when the editor is fully initialized (including the iframe)
21671 * @param {Roo.HtmlEditorCore} this
21676 * Fires when the editor is first receives the focus. Any insertion must wait
21677 * until after this event.
21678 * @param {Roo.HtmlEditorCore} this
21682 * @event beforesync
21683 * Fires before the textarea is updated with content from the editor iframe. Return false
21684 * to cancel the sync.
21685 * @param {Roo.HtmlEditorCore} this
21686 * @param {String} html
21690 * @event beforepush
21691 * Fires before the iframe editor is updated with content from the textarea. Return false
21692 * to cancel the push.
21693 * @param {Roo.HtmlEditorCore} this
21694 * @param {String} html
21699 * Fires when the textarea is updated with content from the editor iframe.
21700 * @param {Roo.HtmlEditorCore} this
21701 * @param {String} html
21706 * Fires when the iframe editor is updated with content from the textarea.
21707 * @param {Roo.HtmlEditorCore} this
21708 * @param {String} html
21713 * @event editorevent
21714 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21715 * @param {Roo.HtmlEditorCore} this
21721 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21723 // defaults : white / black...
21724 this.applyBlacklists();
21731 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21735 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21741 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21746 * @cfg {Number} height (in pixels)
21750 * @cfg {Number} width (in pixels)
21755 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21758 stylesheets: false,
21763 // private properties
21764 validationEvent : false,
21766 initialized : false,
21768 sourceEditMode : false,
21769 onFocus : Roo.emptyFn,
21771 hideMode:'offsets',
21775 // blacklist + whitelisted elements..
21782 * Protected method that will not generally be called directly. It
21783 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21784 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21786 getDocMarkup : function(){
21790 // inherit styels from page...??
21791 if (this.stylesheets === false) {
21793 Roo.get(document.head).select('style').each(function(node) {
21794 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21797 Roo.get(document.head).select('link').each(function(node) {
21798 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21801 } else if (!this.stylesheets.length) {
21803 st = '<style type="text/css">' +
21804 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21807 st = '<style type="text/css">' +
21812 st += '<style type="text/css">' +
21813 'IMG { cursor: pointer } ' +
21816 var cls = 'roo-htmleditor-body';
21818 if(this.bodyCls.length){
21819 cls += ' ' + this.bodyCls;
21822 return '<html><head>' + st +
21823 //<style type="text/css">' +
21824 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21826 ' </head><body class="' + cls + '"></body></html>';
21830 onRender : function(ct, position)
21833 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21834 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21837 this.el.dom.style.border = '0 none';
21838 this.el.dom.setAttribute('tabIndex', -1);
21839 this.el.addClass('x-hidden hide');
21843 if(Roo.isIE){ // fix IE 1px bogus margin
21844 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21848 this.frameId = Roo.id();
21852 var iframe = this.owner.wrap.createChild({
21854 cls: 'form-control', // bootstrap..
21856 name: this.frameId,
21857 frameBorder : 'no',
21858 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21863 this.iframe = iframe.dom;
21865 this.assignDocWin();
21867 this.doc.designMode = 'on';
21870 this.doc.write(this.getDocMarkup());
21874 var task = { // must defer to wait for browser to be ready
21876 //console.log("run task?" + this.doc.readyState);
21877 this.assignDocWin();
21878 if(this.doc.body || this.doc.readyState == 'complete'){
21880 this.doc.designMode="on";
21884 Roo.TaskMgr.stop(task);
21885 this.initEditor.defer(10, this);
21892 Roo.TaskMgr.start(task);
21897 onResize : function(w, h)
21899 Roo.log('resize: ' +w + ',' + h );
21900 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21904 if(typeof w == 'number'){
21906 this.iframe.style.width = w + 'px';
21908 if(typeof h == 'number'){
21910 this.iframe.style.height = h + 'px';
21912 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21919 * Toggles the editor between standard and source edit mode.
21920 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21922 toggleSourceEdit : function(sourceEditMode){
21924 this.sourceEditMode = sourceEditMode === true;
21926 if(this.sourceEditMode){
21928 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21931 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21932 //this.iframe.className = '';
21935 //this.setSize(this.owner.wrap.getSize());
21936 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21943 * Protected method that will not generally be called directly. If you need/want
21944 * custom HTML cleanup, this is the method you should override.
21945 * @param {String} html The HTML to be cleaned
21946 * return {String} The cleaned HTML
21948 cleanHtml : function(html){
21949 html = String(html);
21950 if(html.length > 5){
21951 if(Roo.isSafari){ // strip safari nonsense
21952 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21955 if(html == ' '){
21962 * HTML Editor -> Textarea
21963 * Protected method that will not generally be called directly. Syncs the contents
21964 * of the editor iframe with the textarea.
21966 syncValue : function(){
21967 if(this.initialized){
21968 var bd = (this.doc.body || this.doc.documentElement);
21969 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21970 var html = bd.innerHTML;
21972 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21973 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21975 html = '<div style="'+m[0]+'">' + html + '</div>';
21978 html = this.cleanHtml(html);
21979 // fix up the special chars.. normaly like back quotes in word...
21980 // however we do not want to do this with chinese..
21981 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21982 var cc = b.charCodeAt();
21984 (cc >= 0x4E00 && cc < 0xA000 ) ||
21985 (cc >= 0x3400 && cc < 0x4E00 ) ||
21986 (cc >= 0xf900 && cc < 0xfb00 )
21992 if(this.owner.fireEvent('beforesync', this, html) !== false){
21993 this.el.dom.value = html;
21994 this.owner.fireEvent('sync', this, html);
22000 * Protected method that will not generally be called directly. Pushes the value of the textarea
22001 * into the iframe editor.
22003 pushValue : function(){
22004 if(this.initialized){
22005 var v = this.el.dom.value.trim();
22007 // if(v.length < 1){
22011 if(this.owner.fireEvent('beforepush', this, v) !== false){
22012 var d = (this.doc.body || this.doc.documentElement);
22014 this.cleanUpPaste();
22015 this.el.dom.value = d.innerHTML;
22016 this.owner.fireEvent('push', this, v);
22022 deferFocus : function(){
22023 this.focus.defer(10, this);
22027 focus : function(){
22028 if(this.win && !this.sourceEditMode){
22035 assignDocWin: function()
22037 var iframe = this.iframe;
22040 this.doc = iframe.contentWindow.document;
22041 this.win = iframe.contentWindow;
22043 // if (!Roo.get(this.frameId)) {
22046 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22047 // this.win = Roo.get(this.frameId).dom.contentWindow;
22049 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22053 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22054 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22059 initEditor : function(){
22060 //console.log("INIT EDITOR");
22061 this.assignDocWin();
22065 this.doc.designMode="on";
22067 this.doc.write(this.getDocMarkup());
22070 var dbody = (this.doc.body || this.doc.documentElement);
22071 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22072 // this copies styles from the containing element into thsi one..
22073 // not sure why we need all of this..
22074 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22076 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22077 //ss['background-attachment'] = 'fixed'; // w3c
22078 dbody.bgProperties = 'fixed'; // ie
22079 //Roo.DomHelper.applyStyles(dbody, ss);
22080 Roo.EventManager.on(this.doc, {
22081 //'mousedown': this.onEditorEvent,
22082 'mouseup': this.onEditorEvent,
22083 'dblclick': this.onEditorEvent,
22084 'click': this.onEditorEvent,
22085 'keyup': this.onEditorEvent,
22090 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22092 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22093 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22095 this.initialized = true;
22097 this.owner.fireEvent('initialize', this);
22102 onDestroy : function(){
22108 //for (var i =0; i < this.toolbars.length;i++) {
22109 // // fixme - ask toolbars for heights?
22110 // this.toolbars[i].onDestroy();
22113 //this.wrap.dom.innerHTML = '';
22114 //this.wrap.remove();
22119 onFirstFocus : function(){
22121 this.assignDocWin();
22124 this.activated = true;
22127 if(Roo.isGecko){ // prevent silly gecko errors
22129 var s = this.win.getSelection();
22130 if(!s.focusNode || s.focusNode.nodeType != 3){
22131 var r = s.getRangeAt(0);
22132 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22137 this.execCmd('useCSS', true);
22138 this.execCmd('styleWithCSS', false);
22141 this.owner.fireEvent('activate', this);
22145 adjustFont: function(btn){
22146 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22147 //if(Roo.isSafari){ // safari
22150 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22151 if(Roo.isSafari){ // safari
22152 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22153 v = (v < 10) ? 10 : v;
22154 v = (v > 48) ? 48 : v;
22155 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22160 v = Math.max(1, v+adjust);
22162 this.execCmd('FontSize', v );
22165 onEditorEvent : function(e)
22167 this.owner.fireEvent('editorevent', this, e);
22168 // this.updateToolbar();
22169 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22172 insertTag : function(tg)
22174 // could be a bit smarter... -> wrap the current selected tRoo..
22175 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22177 range = this.createRange(this.getSelection());
22178 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22179 wrappingNode.appendChild(range.extractContents());
22180 range.insertNode(wrappingNode);
22187 this.execCmd("formatblock", tg);
22191 insertText : function(txt)
22195 var range = this.createRange();
22196 range.deleteContents();
22197 //alert(Sender.getAttribute('label'));
22199 range.insertNode(this.doc.createTextNode(txt));
22205 * Executes a Midas editor command on the editor document and performs necessary focus and
22206 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22207 * @param {String} cmd The Midas command
22208 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22210 relayCmd : function(cmd, value){
22212 this.execCmd(cmd, value);
22213 this.owner.fireEvent('editorevent', this);
22214 //this.updateToolbar();
22215 this.owner.deferFocus();
22219 * Executes a Midas editor command directly on the editor document.
22220 * For visual commands, you should use {@link #relayCmd} instead.
22221 * <b>This should only be called after the editor is initialized.</b>
22222 * @param {String} cmd The Midas command
22223 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22225 execCmd : function(cmd, value){
22226 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22233 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22235 * @param {String} text | dom node..
22237 insertAtCursor : function(text)
22240 if(!this.activated){
22246 var r = this.doc.selection.createRange();
22257 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22261 // from jquery ui (MIT licenced)
22263 var win = this.win;
22265 if (win.getSelection && win.getSelection().getRangeAt) {
22266 range = win.getSelection().getRangeAt(0);
22267 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22268 range.insertNode(node);
22269 } else if (win.document.selection && win.document.selection.createRange) {
22270 // no firefox support
22271 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22272 win.document.selection.createRange().pasteHTML(txt);
22274 // no firefox support
22275 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22276 this.execCmd('InsertHTML', txt);
22285 mozKeyPress : function(e){
22287 var c = e.getCharCode(), cmd;
22290 c = String.fromCharCode(c).toLowerCase();
22304 this.cleanUpPaste.defer(100, this);
22312 e.preventDefault();
22320 fixKeys : function(){ // load time branching for fastest keydown performance
22322 return function(e){
22323 var k = e.getKey(), r;
22326 r = this.doc.selection.createRange();
22329 r.pasteHTML('    ');
22336 r = this.doc.selection.createRange();
22338 var target = r.parentElement();
22339 if(!target || target.tagName.toLowerCase() != 'li'){
22341 r.pasteHTML('<br />');
22347 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22348 this.cleanUpPaste.defer(100, this);
22354 }else if(Roo.isOpera){
22355 return function(e){
22356 var k = e.getKey();
22360 this.execCmd('InsertHTML','    ');
22363 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22364 this.cleanUpPaste.defer(100, this);
22369 }else if(Roo.isSafari){
22370 return function(e){
22371 var k = e.getKey();
22375 this.execCmd('InsertText','\t');
22379 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22380 this.cleanUpPaste.defer(100, this);
22388 getAllAncestors: function()
22390 var p = this.getSelectedNode();
22393 a.push(p); // push blank onto stack..
22394 p = this.getParentElement();
22398 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22402 a.push(this.doc.body);
22406 lastSelNode : false,
22409 getSelection : function()
22411 this.assignDocWin();
22412 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22415 getSelectedNode: function()
22417 // this may only work on Gecko!!!
22419 // should we cache this!!!!
22424 var range = this.createRange(this.getSelection()).cloneRange();
22427 var parent = range.parentElement();
22429 var testRange = range.duplicate();
22430 testRange.moveToElementText(parent);
22431 if (testRange.inRange(range)) {
22434 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22437 parent = parent.parentElement;
22442 // is ancestor a text element.
22443 var ac = range.commonAncestorContainer;
22444 if (ac.nodeType == 3) {
22445 ac = ac.parentNode;
22448 var ar = ac.childNodes;
22451 var other_nodes = [];
22452 var has_other_nodes = false;
22453 for (var i=0;i<ar.length;i++) {
22454 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22457 // fullly contained node.
22459 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22464 // probably selected..
22465 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22466 other_nodes.push(ar[i]);
22470 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22475 has_other_nodes = true;
22477 if (!nodes.length && other_nodes.length) {
22478 nodes= other_nodes;
22480 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22486 createRange: function(sel)
22488 // this has strange effects when using with
22489 // top toolbar - not sure if it's a great idea.
22490 //this.editor.contentWindow.focus();
22491 if (typeof sel != "undefined") {
22493 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22495 return this.doc.createRange();
22498 return this.doc.createRange();
22501 getParentElement: function()
22504 this.assignDocWin();
22505 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22507 var range = this.createRange(sel);
22510 var p = range.commonAncestorContainer;
22511 while (p.nodeType == 3) { // text node
22522 * Range intersection.. the hard stuff...
22526 * [ -- selected range --- ]
22530 * if end is before start or hits it. fail.
22531 * if start is after end or hits it fail.
22533 * if either hits (but other is outside. - then it's not
22539 // @see http://www.thismuchiknow.co.uk/?p=64.
22540 rangeIntersectsNode : function(range, node)
22542 var nodeRange = node.ownerDocument.createRange();
22544 nodeRange.selectNode(node);
22546 nodeRange.selectNodeContents(node);
22549 var rangeStartRange = range.cloneRange();
22550 rangeStartRange.collapse(true);
22552 var rangeEndRange = range.cloneRange();
22553 rangeEndRange.collapse(false);
22555 var nodeStartRange = nodeRange.cloneRange();
22556 nodeStartRange.collapse(true);
22558 var nodeEndRange = nodeRange.cloneRange();
22559 nodeEndRange.collapse(false);
22561 return rangeStartRange.compareBoundaryPoints(
22562 Range.START_TO_START, nodeEndRange) == -1 &&
22563 rangeEndRange.compareBoundaryPoints(
22564 Range.START_TO_START, nodeStartRange) == 1;
22568 rangeCompareNode : function(range, node)
22570 var nodeRange = node.ownerDocument.createRange();
22572 nodeRange.selectNode(node);
22574 nodeRange.selectNodeContents(node);
22578 range.collapse(true);
22580 nodeRange.collapse(true);
22582 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22583 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22585 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22587 var nodeIsBefore = ss == 1;
22588 var nodeIsAfter = ee == -1;
22590 if (nodeIsBefore && nodeIsAfter) {
22593 if (!nodeIsBefore && nodeIsAfter) {
22594 return 1; //right trailed.
22597 if (nodeIsBefore && !nodeIsAfter) {
22598 return 2; // left trailed.
22604 // private? - in a new class?
22605 cleanUpPaste : function()
22607 // cleans up the whole document..
22608 Roo.log('cleanuppaste');
22610 this.cleanUpChildren(this.doc.body);
22611 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22612 if (clean != this.doc.body.innerHTML) {
22613 this.doc.body.innerHTML = clean;
22618 cleanWordChars : function(input) {// change the chars to hex code
22619 var he = Roo.HtmlEditorCore;
22621 var output = input;
22622 Roo.each(he.swapCodes, function(sw) {
22623 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22625 output = output.replace(swapper, sw[1]);
22632 cleanUpChildren : function (n)
22634 if (!n.childNodes.length) {
22637 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22638 this.cleanUpChild(n.childNodes[i]);
22645 cleanUpChild : function (node)
22648 //console.log(node);
22649 if (node.nodeName == "#text") {
22650 // clean up silly Windows -- stuff?
22653 if (node.nodeName == "#comment") {
22654 node.parentNode.removeChild(node);
22655 // clean up silly Windows -- stuff?
22658 var lcname = node.tagName.toLowerCase();
22659 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22660 // whitelist of tags..
22662 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22664 node.parentNode.removeChild(node);
22669 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22671 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22672 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22674 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22675 // remove_keep_children = true;
22678 if (remove_keep_children) {
22679 this.cleanUpChildren(node);
22680 // inserts everything just before this node...
22681 while (node.childNodes.length) {
22682 var cn = node.childNodes[0];
22683 node.removeChild(cn);
22684 node.parentNode.insertBefore(cn, node);
22686 node.parentNode.removeChild(node);
22690 if (!node.attributes || !node.attributes.length) {
22691 this.cleanUpChildren(node);
22695 function cleanAttr(n,v)
22698 if (v.match(/^\./) || v.match(/^\//)) {
22701 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22704 if (v.match(/^#/)) {
22707 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22708 node.removeAttribute(n);
22712 var cwhite = this.cwhite;
22713 var cblack = this.cblack;
22715 function cleanStyle(n,v)
22717 if (v.match(/expression/)) { //XSS?? should we even bother..
22718 node.removeAttribute(n);
22722 var parts = v.split(/;/);
22725 Roo.each(parts, function(p) {
22726 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22730 var l = p.split(':').shift().replace(/\s+/g,'');
22731 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22733 if ( cwhite.length && cblack.indexOf(l) > -1) {
22734 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22735 //node.removeAttribute(n);
22739 // only allow 'c whitelisted system attributes'
22740 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22741 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22742 //node.removeAttribute(n);
22752 if (clean.length) {
22753 node.setAttribute(n, clean.join(';'));
22755 node.removeAttribute(n);
22761 for (var i = node.attributes.length-1; i > -1 ; i--) {
22762 var a = node.attributes[i];
22765 if (a.name.toLowerCase().substr(0,2)=='on') {
22766 node.removeAttribute(a.name);
22769 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22770 node.removeAttribute(a.name);
22773 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22774 cleanAttr(a.name,a.value); // fixme..
22777 if (a.name == 'style') {
22778 cleanStyle(a.name,a.value);
22781 /// clean up MS crap..
22782 // tecnically this should be a list of valid class'es..
22785 if (a.name == 'class') {
22786 if (a.value.match(/^Mso/)) {
22787 node.className = '';
22790 if (a.value.match(/^body$/)) {
22791 node.className = '';
22802 this.cleanUpChildren(node);
22808 * Clean up MS wordisms...
22810 cleanWord : function(node)
22815 this.cleanWord(this.doc.body);
22818 if (node.nodeName == "#text") {
22819 // clean up silly Windows -- stuff?
22822 if (node.nodeName == "#comment") {
22823 node.parentNode.removeChild(node);
22824 // clean up silly Windows -- stuff?
22828 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22829 node.parentNode.removeChild(node);
22833 // remove - but keep children..
22834 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22835 while (node.childNodes.length) {
22836 var cn = node.childNodes[0];
22837 node.removeChild(cn);
22838 node.parentNode.insertBefore(cn, node);
22840 node.parentNode.removeChild(node);
22841 this.iterateChildren(node, this.cleanWord);
22845 if (node.className.length) {
22847 var cn = node.className.split(/\W+/);
22849 Roo.each(cn, function(cls) {
22850 if (cls.match(/Mso[a-zA-Z]+/)) {
22855 node.className = cna.length ? cna.join(' ') : '';
22857 node.removeAttribute("class");
22861 if (node.hasAttribute("lang")) {
22862 node.removeAttribute("lang");
22865 if (node.hasAttribute("style")) {
22867 var styles = node.getAttribute("style").split(";");
22869 Roo.each(styles, function(s) {
22870 if (!s.match(/:/)) {
22873 var kv = s.split(":");
22874 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22877 // what ever is left... we allow.
22880 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22881 if (!nstyle.length) {
22882 node.removeAttribute('style');
22885 this.iterateChildren(node, this.cleanWord);
22891 * iterateChildren of a Node, calling fn each time, using this as the scole..
22892 * @param {DomNode} node node to iterate children of.
22893 * @param {Function} fn method of this class to call on each item.
22895 iterateChildren : function(node, fn)
22897 if (!node.childNodes.length) {
22900 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22901 fn.call(this, node.childNodes[i])
22907 * cleanTableWidths.
22909 * Quite often pasting from word etc.. results in tables with column and widths.
22910 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22913 cleanTableWidths : function(node)
22918 this.cleanTableWidths(this.doc.body);
22923 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22926 Roo.log(node.tagName);
22927 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22928 this.iterateChildren(node, this.cleanTableWidths);
22931 if (node.hasAttribute('width')) {
22932 node.removeAttribute('width');
22936 if (node.hasAttribute("style")) {
22939 var styles = node.getAttribute("style").split(";");
22941 Roo.each(styles, function(s) {
22942 if (!s.match(/:/)) {
22945 var kv = s.split(":");
22946 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22949 // what ever is left... we allow.
22952 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22953 if (!nstyle.length) {
22954 node.removeAttribute('style');
22958 this.iterateChildren(node, this.cleanTableWidths);
22966 domToHTML : function(currentElement, depth, nopadtext) {
22968 depth = depth || 0;
22969 nopadtext = nopadtext || false;
22971 if (!currentElement) {
22972 return this.domToHTML(this.doc.body);
22975 //Roo.log(currentElement);
22977 var allText = false;
22978 var nodeName = currentElement.nodeName;
22979 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22981 if (nodeName == '#text') {
22983 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22988 if (nodeName != 'BODY') {
22991 // Prints the node tagName, such as <A>, <IMG>, etc
22994 for(i = 0; i < currentElement.attributes.length;i++) {
22996 var aname = currentElement.attributes.item(i).name;
22997 if (!currentElement.attributes.item(i).value.length) {
23000 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23003 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23012 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23015 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23020 // Traverse the tree
23022 var currentElementChild = currentElement.childNodes.item(i);
23023 var allText = true;
23024 var innerHTML = '';
23026 while (currentElementChild) {
23027 // Formatting code (indent the tree so it looks nice on the screen)
23028 var nopad = nopadtext;
23029 if (lastnode == 'SPAN') {
23033 if (currentElementChild.nodeName == '#text') {
23034 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23035 toadd = nopadtext ? toadd : toadd.trim();
23036 if (!nopad && toadd.length > 80) {
23037 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23039 innerHTML += toadd;
23042 currentElementChild = currentElement.childNodes.item(i);
23048 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23050 // Recursively traverse the tree structure of the child node
23051 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23052 lastnode = currentElementChild.nodeName;
23054 currentElementChild=currentElement.childNodes.item(i);
23060 // The remaining code is mostly for formatting the tree
23061 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23066 ret+= "</"+tagName+">";
23072 applyBlacklists : function()
23074 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23075 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23079 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23080 if (b.indexOf(tag) > -1) {
23083 this.white.push(tag);
23087 Roo.each(w, function(tag) {
23088 if (b.indexOf(tag) > -1) {
23091 if (this.white.indexOf(tag) > -1) {
23094 this.white.push(tag);
23099 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23100 if (w.indexOf(tag) > -1) {
23103 this.black.push(tag);
23107 Roo.each(b, function(tag) {
23108 if (w.indexOf(tag) > -1) {
23111 if (this.black.indexOf(tag) > -1) {
23114 this.black.push(tag);
23119 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23120 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23124 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23125 if (b.indexOf(tag) > -1) {
23128 this.cwhite.push(tag);
23132 Roo.each(w, function(tag) {
23133 if (b.indexOf(tag) > -1) {
23136 if (this.cwhite.indexOf(tag) > -1) {
23139 this.cwhite.push(tag);
23144 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23145 if (w.indexOf(tag) > -1) {
23148 this.cblack.push(tag);
23152 Roo.each(b, function(tag) {
23153 if (w.indexOf(tag) > -1) {
23156 if (this.cblack.indexOf(tag) > -1) {
23159 this.cblack.push(tag);
23164 setStylesheets : function(stylesheets)
23166 if(typeof(stylesheets) == 'string'){
23167 Roo.get(this.iframe.contentDocument.head).createChild({
23169 rel : 'stylesheet',
23178 Roo.each(stylesheets, function(s) {
23183 Roo.get(_this.iframe.contentDocument.head).createChild({
23185 rel : 'stylesheet',
23194 removeStylesheets : function()
23198 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23203 setStyle : function(style)
23205 Roo.get(this.iframe.contentDocument.head).createChild({
23214 // hide stuff that is not compatible
23228 * @event specialkey
23232 * @cfg {String} fieldClass @hide
23235 * @cfg {String} focusClass @hide
23238 * @cfg {String} autoCreate @hide
23241 * @cfg {String} inputType @hide
23244 * @cfg {String} invalidClass @hide
23247 * @cfg {String} invalidText @hide
23250 * @cfg {String} msgFx @hide
23253 * @cfg {String} validateOnBlur @hide
23257 Roo.HtmlEditorCore.white = [
23258 'area', 'br', 'img', 'input', 'hr', 'wbr',
23260 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23261 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23262 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23263 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23264 'table', 'ul', 'xmp',
23266 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23269 'dir', 'menu', 'ol', 'ul', 'dl',
23275 Roo.HtmlEditorCore.black = [
23276 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23278 'base', 'basefont', 'bgsound', 'blink', 'body',
23279 'frame', 'frameset', 'head', 'html', 'ilayer',
23280 'iframe', 'layer', 'link', 'meta', 'object',
23281 'script', 'style' ,'title', 'xml' // clean later..
23283 Roo.HtmlEditorCore.clean = [
23284 'script', 'style', 'title', 'xml'
23286 Roo.HtmlEditorCore.remove = [
23291 Roo.HtmlEditorCore.ablack = [
23295 Roo.HtmlEditorCore.aclean = [
23296 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23300 Roo.HtmlEditorCore.pwhite= [
23301 'http', 'https', 'mailto'
23304 // white listed style attributes.
23305 Roo.HtmlEditorCore.cwhite= [
23306 // 'text-align', /// default is to allow most things..
23312 // black listed style attributes.
23313 Roo.HtmlEditorCore.cblack= [
23314 // 'font-size' -- this can be set by the project
23318 Roo.HtmlEditorCore.swapCodes =[
23337 * @class Roo.bootstrap.HtmlEditor
23338 * @extends Roo.bootstrap.TextArea
23339 * Bootstrap HtmlEditor class
23342 * Create a new HtmlEditor
23343 * @param {Object} config The config object
23346 Roo.bootstrap.HtmlEditor = function(config){
23347 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23348 if (!this.toolbars) {
23349 this.toolbars = [];
23352 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23355 * @event initialize
23356 * Fires when the editor is fully initialized (including the iframe)
23357 * @param {HtmlEditor} this
23362 * Fires when the editor is first receives the focus. Any insertion must wait
23363 * until after this event.
23364 * @param {HtmlEditor} this
23368 * @event beforesync
23369 * Fires before the textarea is updated with content from the editor iframe. Return false
23370 * to cancel the sync.
23371 * @param {HtmlEditor} this
23372 * @param {String} html
23376 * @event beforepush
23377 * Fires before the iframe editor is updated with content from the textarea. Return false
23378 * to cancel the push.
23379 * @param {HtmlEditor} this
23380 * @param {String} html
23385 * Fires when the textarea is updated with content from the editor iframe.
23386 * @param {HtmlEditor} this
23387 * @param {String} html
23392 * Fires when the iframe editor is updated with content from the textarea.
23393 * @param {HtmlEditor} this
23394 * @param {String} html
23398 * @event editmodechange
23399 * Fires when the editor switches edit modes
23400 * @param {HtmlEditor} this
23401 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23403 editmodechange: true,
23405 * @event editorevent
23406 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23407 * @param {HtmlEditor} this
23411 * @event firstfocus
23412 * Fires when on first focus - needed by toolbars..
23413 * @param {HtmlEditor} this
23418 * Auto save the htmlEditor value as a file into Events
23419 * @param {HtmlEditor} this
23423 * @event savedpreview
23424 * preview the saved version of htmlEditor
23425 * @param {HtmlEditor} this
23432 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23436 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23441 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23446 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23451 * @cfg {Number} height (in pixels)
23455 * @cfg {Number} width (in pixels)
23460 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23463 stylesheets: false,
23468 // private properties
23469 validationEvent : false,
23471 initialized : false,
23474 onFocus : Roo.emptyFn,
23476 hideMode:'offsets',
23478 tbContainer : false,
23482 toolbarContainer :function() {
23483 return this.wrap.select('.x-html-editor-tb',true).first();
23487 * Protected method that will not generally be called directly. It
23488 * is called when the editor creates its toolbar. Override this method if you need to
23489 * add custom toolbar buttons.
23490 * @param {HtmlEditor} editor
23492 createToolbar : function(){
23493 Roo.log('renewing');
23494 Roo.log("create toolbars");
23496 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23497 this.toolbars[0].render(this.toolbarContainer());
23501 // if (!editor.toolbars || !editor.toolbars.length) {
23502 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23505 // for (var i =0 ; i < editor.toolbars.length;i++) {
23506 // editor.toolbars[i] = Roo.factory(
23507 // typeof(editor.toolbars[i]) == 'string' ?
23508 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23509 // Roo.bootstrap.HtmlEditor);
23510 // editor.toolbars[i].init(editor);
23516 onRender : function(ct, position)
23518 // Roo.log("Call onRender: " + this.xtype);
23520 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23522 this.wrap = this.inputEl().wrap({
23523 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23526 this.editorcore.onRender(ct, position);
23528 if (this.resizable) {
23529 this.resizeEl = new Roo.Resizable(this.wrap, {
23533 minHeight : this.height,
23534 height: this.height,
23535 handles : this.resizable,
23538 resize : function(r, w, h) {
23539 _t.onResize(w,h); // -something
23545 this.createToolbar(this);
23548 if(!this.width && this.resizable){
23549 this.setSize(this.wrap.getSize());
23551 if (this.resizeEl) {
23552 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23553 // should trigger onReize..
23559 onResize : function(w, h)
23561 Roo.log('resize: ' +w + ',' + h );
23562 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23566 if(this.inputEl() ){
23567 if(typeof w == 'number'){
23568 var aw = w - this.wrap.getFrameWidth('lr');
23569 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23572 if(typeof h == 'number'){
23573 var tbh = -11; // fixme it needs to tool bar size!
23574 for (var i =0; i < this.toolbars.length;i++) {
23575 // fixme - ask toolbars for heights?
23576 tbh += this.toolbars[i].el.getHeight();
23577 //if (this.toolbars[i].footer) {
23578 // tbh += this.toolbars[i].footer.el.getHeight();
23586 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23587 ah -= 5; // knock a few pixes off for look..
23588 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23592 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23593 this.editorcore.onResize(ew,eh);
23598 * Toggles the editor between standard and source edit mode.
23599 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23601 toggleSourceEdit : function(sourceEditMode)
23603 this.editorcore.toggleSourceEdit(sourceEditMode);
23605 if(this.editorcore.sourceEditMode){
23606 Roo.log('editor - showing textarea');
23609 // Roo.log(this.syncValue());
23611 this.inputEl().removeClass(['hide', 'x-hidden']);
23612 this.inputEl().dom.removeAttribute('tabIndex');
23613 this.inputEl().focus();
23615 Roo.log('editor - hiding textarea');
23617 // Roo.log(this.pushValue());
23620 this.inputEl().addClass(['hide', 'x-hidden']);
23621 this.inputEl().dom.setAttribute('tabIndex', -1);
23622 //this.deferFocus();
23625 if(this.resizable){
23626 this.setSize(this.wrap.getSize());
23629 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23632 // private (for BoxComponent)
23633 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23635 // private (for BoxComponent)
23636 getResizeEl : function(){
23640 // private (for BoxComponent)
23641 getPositionEl : function(){
23646 initEvents : function(){
23647 this.originalValue = this.getValue();
23651 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23654 // markInvalid : Roo.emptyFn,
23656 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23659 // clearInvalid : Roo.emptyFn,
23661 setValue : function(v){
23662 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23663 this.editorcore.pushValue();
23668 deferFocus : function(){
23669 this.focus.defer(10, this);
23673 focus : function(){
23674 this.editorcore.focus();
23680 onDestroy : function(){
23686 for (var i =0; i < this.toolbars.length;i++) {
23687 // fixme - ask toolbars for heights?
23688 this.toolbars[i].onDestroy();
23691 this.wrap.dom.innerHTML = '';
23692 this.wrap.remove();
23697 onFirstFocus : function(){
23698 //Roo.log("onFirstFocus");
23699 this.editorcore.onFirstFocus();
23700 for (var i =0; i < this.toolbars.length;i++) {
23701 this.toolbars[i].onFirstFocus();
23707 syncValue : function()
23709 this.editorcore.syncValue();
23712 pushValue : function()
23714 this.editorcore.pushValue();
23718 // hide stuff that is not compatible
23732 * @event specialkey
23736 * @cfg {String} fieldClass @hide
23739 * @cfg {String} focusClass @hide
23742 * @cfg {String} autoCreate @hide
23745 * @cfg {String} inputType @hide
23748 * @cfg {String} invalidClass @hide
23751 * @cfg {String} invalidText @hide
23754 * @cfg {String} msgFx @hide
23757 * @cfg {String} validateOnBlur @hide
23766 Roo.namespace('Roo.bootstrap.htmleditor');
23768 * @class Roo.bootstrap.HtmlEditorToolbar1
23773 new Roo.bootstrap.HtmlEditor({
23776 new Roo.bootstrap.HtmlEditorToolbar1({
23777 disable : { fonts: 1 , format: 1, ..., ... , ...],
23783 * @cfg {Object} disable List of elements to disable..
23784 * @cfg {Array} btns List of additional buttons.
23788 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23791 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23794 Roo.apply(this, config);
23796 // default disabled, based on 'good practice'..
23797 this.disable = this.disable || {};
23798 Roo.applyIf(this.disable, {
23801 specialElements : true
23803 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23805 this.editor = config.editor;
23806 this.editorcore = config.editor.editorcore;
23808 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23810 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23811 // dont call parent... till later.
23813 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23818 editorcore : false,
23823 "h1","h2","h3","h4","h5","h6",
23825 "abbr", "acronym", "address", "cite", "samp", "var",
23829 onRender : function(ct, position)
23831 // Roo.log("Call onRender: " + this.xtype);
23833 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23835 this.el.dom.style.marginBottom = '0';
23837 var editorcore = this.editorcore;
23838 var editor= this.editor;
23841 var btn = function(id,cmd , toggle, handler, html){
23843 var event = toggle ? 'toggle' : 'click';
23848 xns: Roo.bootstrap,
23851 enableToggle:toggle !== false,
23853 pressed : toggle ? false : null,
23856 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23857 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23863 // var cb_box = function...
23868 xns: Roo.bootstrap,
23869 glyphicon : 'font',
23873 xns: Roo.bootstrap,
23877 Roo.each(this.formats, function(f) {
23878 style.menu.items.push({
23880 xns: Roo.bootstrap,
23881 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23886 editorcore.insertTag(this.tagname);
23893 children.push(style);
23895 btn('bold',false,true);
23896 btn('italic',false,true);
23897 btn('align-left', 'justifyleft',true);
23898 btn('align-center', 'justifycenter',true);
23899 btn('align-right' , 'justifyright',true);
23900 btn('link', false, false, function(btn) {
23901 //Roo.log("create link?");
23902 var url = prompt(this.createLinkText, this.defaultLinkValue);
23903 if(url && url != 'http:/'+'/'){
23904 this.editorcore.relayCmd('createlink', url);
23907 btn('list','insertunorderedlist',true);
23908 btn('pencil', false,true, function(btn){
23910 this.toggleSourceEdit(btn.pressed);
23913 if (this.editor.btns.length > 0) {
23914 for (var i = 0; i<this.editor.btns.length; i++) {
23915 children.push(this.editor.btns[i]);
23923 xns: Roo.bootstrap,
23928 xns: Roo.bootstrap,
23933 cog.menu.items.push({
23935 xns: Roo.bootstrap,
23936 html : Clean styles,
23941 editorcore.insertTag(this.tagname);
23950 this.xtype = 'NavSimplebar';
23952 for(var i=0;i< children.length;i++) {
23954 this.buttons.add(this.addxtypeChild(children[i]));
23958 editor.on('editorevent', this.updateToolbar, this);
23960 onBtnClick : function(id)
23962 this.editorcore.relayCmd(id);
23963 this.editorcore.focus();
23967 * Protected method that will not generally be called directly. It triggers
23968 * a toolbar update by reading the markup state of the current selection in the editor.
23970 updateToolbar: function(){
23972 if(!this.editorcore.activated){
23973 this.editor.onFirstFocus(); // is this neeed?
23977 var btns = this.buttons;
23978 var doc = this.editorcore.doc;
23979 btns.get('bold').setActive(doc.queryCommandState('bold'));
23980 btns.get('italic').setActive(doc.queryCommandState('italic'));
23981 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23983 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23984 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23985 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23987 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23988 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23991 var ans = this.editorcore.getAllAncestors();
23992 if (this.formatCombo) {
23995 var store = this.formatCombo.store;
23996 this.formatCombo.setValue("");
23997 for (var i =0; i < ans.length;i++) {
23998 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24000 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24008 // hides menus... - so this cant be on a menu...
24009 Roo.bootstrap.MenuMgr.hideAll();
24011 Roo.bootstrap.MenuMgr.hideAll();
24012 //this.editorsyncValue();
24014 onFirstFocus: function() {
24015 this.buttons.each(function(item){
24019 toggleSourceEdit : function(sourceEditMode){
24022 if(sourceEditMode){
24023 Roo.log("disabling buttons");
24024 this.buttons.each( function(item){
24025 if(item.cmd != 'pencil'){
24031 Roo.log("enabling buttons");
24032 if(this.editorcore.initialized){
24033 this.buttons.each( function(item){
24039 Roo.log("calling toggole on editor");
24040 // tell the editor that it's been pressed..
24041 this.editor.toggleSourceEdit(sourceEditMode);
24051 * @class Roo.bootstrap.Table.AbstractSelectionModel
24052 * @extends Roo.util.Observable
24053 * Abstract base class for grid SelectionModels. It provides the interface that should be
24054 * implemented by descendant classes. This class should not be directly instantiated.
24057 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24058 this.locked = false;
24059 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24063 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24064 /** @ignore Called by the grid automatically. Do not call directly. */
24065 init : function(grid){
24071 * Locks the selections.
24074 this.locked = true;
24078 * Unlocks the selections.
24080 unlock : function(){
24081 this.locked = false;
24085 * Returns true if the selections are locked.
24086 * @return {Boolean}
24088 isLocked : function(){
24089 return this.locked;
24093 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24094 * @class Roo.bootstrap.Table.RowSelectionModel
24095 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24096 * It supports multiple selections and keyboard selection/navigation.
24098 * @param {Object} config
24101 Roo.bootstrap.Table.RowSelectionModel = function(config){
24102 Roo.apply(this, config);
24103 this.selections = new Roo.util.MixedCollection(false, function(o){
24108 this.lastActive = false;
24112 * @event selectionchange
24113 * Fires when the selection changes
24114 * @param {SelectionModel} this
24116 "selectionchange" : true,
24118 * @event afterselectionchange
24119 * Fires after the selection changes (eg. by key press or clicking)
24120 * @param {SelectionModel} this
24122 "afterselectionchange" : true,
24124 * @event beforerowselect
24125 * Fires when a row is selected being selected, return false to cancel.
24126 * @param {SelectionModel} this
24127 * @param {Number} rowIndex The selected index
24128 * @param {Boolean} keepExisting False if other selections will be cleared
24130 "beforerowselect" : true,
24133 * Fires when a row is selected.
24134 * @param {SelectionModel} this
24135 * @param {Number} rowIndex The selected index
24136 * @param {Roo.data.Record} r The record
24138 "rowselect" : true,
24140 * @event rowdeselect
24141 * Fires when a row is deselected.
24142 * @param {SelectionModel} this
24143 * @param {Number} rowIndex The selected index
24145 "rowdeselect" : true
24147 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24148 this.locked = false;
24151 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24153 * @cfg {Boolean} singleSelect
24154 * True to allow selection of only one row at a time (defaults to false)
24156 singleSelect : false,
24159 initEvents : function()
24162 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24163 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24164 //}else{ // allow click to work like normal
24165 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24167 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24168 this.grid.on("rowclick", this.handleMouseDown, this);
24170 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24171 "up" : function(e){
24173 this.selectPrevious(e.shiftKey);
24174 }else if(this.last !== false && this.lastActive !== false){
24175 var last = this.last;
24176 this.selectRange(this.last, this.lastActive-1);
24177 this.grid.getView().focusRow(this.lastActive);
24178 if(last !== false){
24182 this.selectFirstRow();
24184 this.fireEvent("afterselectionchange", this);
24186 "down" : function(e){
24188 this.selectNext(e.shiftKey);
24189 }else if(this.last !== false && this.lastActive !== false){
24190 var last = this.last;
24191 this.selectRange(this.last, this.lastActive+1);
24192 this.grid.getView().focusRow(this.lastActive);
24193 if(last !== false){
24197 this.selectFirstRow();
24199 this.fireEvent("afterselectionchange", this);
24203 this.grid.store.on('load', function(){
24204 this.selections.clear();
24207 var view = this.grid.view;
24208 view.on("refresh", this.onRefresh, this);
24209 view.on("rowupdated", this.onRowUpdated, this);
24210 view.on("rowremoved", this.onRemove, this);
24215 onRefresh : function()
24217 var ds = this.grid.store, i, v = this.grid.view;
24218 var s = this.selections;
24219 s.each(function(r){
24220 if((i = ds.indexOfId(r.id)) != -1){
24229 onRemove : function(v, index, r){
24230 this.selections.remove(r);
24234 onRowUpdated : function(v, index, r){
24235 if(this.isSelected(r)){
24236 v.onRowSelect(index);
24242 * @param {Array} records The records to select
24243 * @param {Boolean} keepExisting (optional) True to keep existing selections
24245 selectRecords : function(records, keepExisting)
24248 this.clearSelections();
24250 var ds = this.grid.store;
24251 for(var i = 0, len = records.length; i < len; i++){
24252 this.selectRow(ds.indexOf(records[i]), true);
24257 * Gets the number of selected rows.
24260 getCount : function(){
24261 return this.selections.length;
24265 * Selects the first row in the grid.
24267 selectFirstRow : function(){
24272 * Select the last row.
24273 * @param {Boolean} keepExisting (optional) True to keep existing selections
24275 selectLastRow : function(keepExisting){
24276 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24277 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24281 * Selects the row immediately following the last selected row.
24282 * @param {Boolean} keepExisting (optional) True to keep existing selections
24284 selectNext : function(keepExisting)
24286 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24287 this.selectRow(this.last+1, keepExisting);
24288 this.grid.getView().focusRow(this.last);
24293 * Selects the row that precedes the last selected row.
24294 * @param {Boolean} keepExisting (optional) True to keep existing selections
24296 selectPrevious : function(keepExisting){
24298 this.selectRow(this.last-1, keepExisting);
24299 this.grid.getView().focusRow(this.last);
24304 * Returns the selected records
24305 * @return {Array} Array of selected records
24307 getSelections : function(){
24308 return [].concat(this.selections.items);
24312 * Returns the first selected record.
24315 getSelected : function(){
24316 return this.selections.itemAt(0);
24321 * Clears all selections.
24323 clearSelections : function(fast)
24329 var ds = this.grid.store;
24330 var s = this.selections;
24331 s.each(function(r){
24332 this.deselectRow(ds.indexOfId(r.id));
24336 this.selections.clear();
24343 * Selects all rows.
24345 selectAll : function(){
24349 this.selections.clear();
24350 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24351 this.selectRow(i, true);
24356 * Returns True if there is a selection.
24357 * @return {Boolean}
24359 hasSelection : function(){
24360 return this.selections.length > 0;
24364 * Returns True if the specified row is selected.
24365 * @param {Number/Record} record The record or index of the record to check
24366 * @return {Boolean}
24368 isSelected : function(index){
24369 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24370 return (r && this.selections.key(r.id) ? true : false);
24374 * Returns True if the specified record id is selected.
24375 * @param {String} id The id of record to check
24376 * @return {Boolean}
24378 isIdSelected : function(id){
24379 return (this.selections.key(id) ? true : false);
24384 handleMouseDBClick : function(e, t){
24388 handleMouseDown : function(e, t)
24390 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24391 if(this.isLocked() || rowIndex < 0 ){
24394 if(e.shiftKey && this.last !== false){
24395 var last = this.last;
24396 this.selectRange(last, rowIndex, e.ctrlKey);
24397 this.last = last; // reset the last
24401 var isSelected = this.isSelected(rowIndex);
24402 //Roo.log("select row:" + rowIndex);
24404 this.deselectRow(rowIndex);
24406 this.selectRow(rowIndex, true);
24410 if(e.button !== 0 && isSelected){
24411 alert('rowIndex 2: ' + rowIndex);
24412 view.focusRow(rowIndex);
24413 }else if(e.ctrlKey && isSelected){
24414 this.deselectRow(rowIndex);
24415 }else if(!isSelected){
24416 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24417 view.focusRow(rowIndex);
24421 this.fireEvent("afterselectionchange", this);
24424 handleDragableRowClick : function(grid, rowIndex, e)
24426 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24427 this.selectRow(rowIndex, false);
24428 grid.view.focusRow(rowIndex);
24429 this.fireEvent("afterselectionchange", this);
24434 * Selects multiple rows.
24435 * @param {Array} rows Array of the indexes of the row to select
24436 * @param {Boolean} keepExisting (optional) True to keep existing selections
24438 selectRows : function(rows, keepExisting){
24440 this.clearSelections();
24442 for(var i = 0, len = rows.length; i < len; i++){
24443 this.selectRow(rows[i], true);
24448 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24449 * @param {Number} startRow The index of the first row in the range
24450 * @param {Number} endRow The index of the last row in the range
24451 * @param {Boolean} keepExisting (optional) True to retain existing selections
24453 selectRange : function(startRow, endRow, keepExisting){
24458 this.clearSelections();
24460 if(startRow <= endRow){
24461 for(var i = startRow; i <= endRow; i++){
24462 this.selectRow(i, true);
24465 for(var i = startRow; i >= endRow; i--){
24466 this.selectRow(i, true);
24472 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24473 * @param {Number} startRow The index of the first row in the range
24474 * @param {Number} endRow The index of the last row in the range
24476 deselectRange : function(startRow, endRow, preventViewNotify){
24480 for(var i = startRow; i <= endRow; i++){
24481 this.deselectRow(i, preventViewNotify);
24487 * @param {Number} row The index of the row to select
24488 * @param {Boolean} keepExisting (optional) True to keep existing selections
24490 selectRow : function(index, keepExisting, preventViewNotify)
24492 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24495 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24496 if(!keepExisting || this.singleSelect){
24497 this.clearSelections();
24500 var r = this.grid.store.getAt(index);
24501 //console.log('selectRow - record id :' + r.id);
24503 this.selections.add(r);
24504 this.last = this.lastActive = index;
24505 if(!preventViewNotify){
24506 var proxy = new Roo.Element(
24507 this.grid.getRowDom(index)
24509 proxy.addClass('bg-info info');
24511 this.fireEvent("rowselect", this, index, r);
24512 this.fireEvent("selectionchange", this);
24518 * @param {Number} row The index of the row to deselect
24520 deselectRow : function(index, preventViewNotify)
24525 if(this.last == index){
24528 if(this.lastActive == index){
24529 this.lastActive = false;
24532 var r = this.grid.store.getAt(index);
24537 this.selections.remove(r);
24538 //.console.log('deselectRow - record id :' + r.id);
24539 if(!preventViewNotify){
24541 var proxy = new Roo.Element(
24542 this.grid.getRowDom(index)
24544 proxy.removeClass('bg-info info');
24546 this.fireEvent("rowdeselect", this, index);
24547 this.fireEvent("selectionchange", this);
24551 restoreLast : function(){
24553 this.last = this._last;
24558 acceptsNav : function(row, col, cm){
24559 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24563 onEditorKey : function(field, e){
24564 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24569 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24571 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24573 }else if(k == e.ENTER && !e.ctrlKey){
24577 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24579 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24581 }else if(k == e.ESC){
24585 g.startEditing(newCell[0], newCell[1]);
24591 * Ext JS Library 1.1.1
24592 * Copyright(c) 2006-2007, Ext JS, LLC.
24594 * Originally Released Under LGPL - original licence link has changed is not relivant.
24597 * <script type="text/javascript">
24601 * @class Roo.bootstrap.PagingToolbar
24602 * @extends Roo.bootstrap.NavSimplebar
24603 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24605 * Create a new PagingToolbar
24606 * @param {Object} config The config object
24607 * @param {Roo.data.Store} store
24609 Roo.bootstrap.PagingToolbar = function(config)
24611 // old args format still supported... - xtype is prefered..
24612 // created from xtype...
24614 this.ds = config.dataSource;
24616 if (config.store && !this.ds) {
24617 this.store= Roo.factory(config.store, Roo.data);
24618 this.ds = this.store;
24619 this.ds.xmodule = this.xmodule || false;
24622 this.toolbarItems = [];
24623 if (config.items) {
24624 this.toolbarItems = config.items;
24627 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24632 this.bind(this.ds);
24635 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24639 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24641 * @cfg {Roo.data.Store} dataSource
24642 * The underlying data store providing the paged data
24645 * @cfg {String/HTMLElement/Element} container
24646 * container The id or element that will contain the toolbar
24649 * @cfg {Boolean} displayInfo
24650 * True to display the displayMsg (defaults to false)
24653 * @cfg {Number} pageSize
24654 * The number of records to display per page (defaults to 20)
24658 * @cfg {String} displayMsg
24659 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24661 displayMsg : 'Displaying {0} - {1} of {2}',
24663 * @cfg {String} emptyMsg
24664 * The message to display when no records are found (defaults to "No data to display")
24666 emptyMsg : 'No data to display',
24668 * Customizable piece of the default paging text (defaults to "Page")
24671 beforePageText : "Page",
24673 * Customizable piece of the default paging text (defaults to "of %0")
24676 afterPageText : "of {0}",
24678 * Customizable piece of the default paging text (defaults to "First Page")
24681 firstText : "First Page",
24683 * Customizable piece of the default paging text (defaults to "Previous Page")
24686 prevText : "Previous Page",
24688 * Customizable piece of the default paging text (defaults to "Next Page")
24691 nextText : "Next Page",
24693 * Customizable piece of the default paging text (defaults to "Last Page")
24696 lastText : "Last Page",
24698 * Customizable piece of the default paging text (defaults to "Refresh")
24701 refreshText : "Refresh",
24705 onRender : function(ct, position)
24707 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24708 this.navgroup.parentId = this.id;
24709 this.navgroup.onRender(this.el, null);
24710 // add the buttons to the navgroup
24712 if(this.displayInfo){
24713 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24714 this.displayEl = this.el.select('.x-paging-info', true).first();
24715 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24716 // this.displayEl = navel.el.select('span',true).first();
24722 Roo.each(_this.buttons, function(e){ // this might need to use render????
24723 Roo.factory(e).render(_this.el);
24727 Roo.each(_this.toolbarItems, function(e) {
24728 _this.navgroup.addItem(e);
24732 this.first = this.navgroup.addItem({
24733 tooltip: this.firstText,
24735 icon : 'fa fa-backward',
24737 preventDefault: true,
24738 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24741 this.prev = this.navgroup.addItem({
24742 tooltip: this.prevText,
24744 icon : 'fa fa-step-backward',
24746 preventDefault: true,
24747 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24749 //this.addSeparator();
24752 var field = this.navgroup.addItem( {
24754 cls : 'x-paging-position',
24756 html : this.beforePageText +
24757 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24758 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24761 this.field = field.el.select('input', true).first();
24762 this.field.on("keydown", this.onPagingKeydown, this);
24763 this.field.on("focus", function(){this.dom.select();});
24766 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24767 //this.field.setHeight(18);
24768 //this.addSeparator();
24769 this.next = this.navgroup.addItem({
24770 tooltip: this.nextText,
24772 html : ' <i class="fa fa-step-forward">',
24774 preventDefault: true,
24775 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24777 this.last = this.navgroup.addItem({
24778 tooltip: this.lastText,
24779 icon : 'fa fa-forward',
24782 preventDefault: true,
24783 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24785 //this.addSeparator();
24786 this.loading = this.navgroup.addItem({
24787 tooltip: this.refreshText,
24788 icon: 'fa fa-refresh',
24789 preventDefault: true,
24790 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24796 updateInfo : function(){
24797 if(this.displayEl){
24798 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24799 var msg = count == 0 ?
24803 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24805 this.displayEl.update(msg);
24810 onLoad : function(ds, r, o)
24812 this.cursor = o.params.start ? o.params.start : 0;
24814 var d = this.getPageData(),
24819 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24820 this.field.dom.value = ap;
24821 this.first.setDisabled(ap == 1);
24822 this.prev.setDisabled(ap == 1);
24823 this.next.setDisabled(ap == ps);
24824 this.last.setDisabled(ap == ps);
24825 this.loading.enable();
24830 getPageData : function(){
24831 var total = this.ds.getTotalCount();
24834 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24835 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24840 onLoadError : function(){
24841 this.loading.enable();
24845 onPagingKeydown : function(e){
24846 var k = e.getKey();
24847 var d = this.getPageData();
24849 var v = this.field.dom.value, pageNum;
24850 if(!v || isNaN(pageNum = parseInt(v, 10))){
24851 this.field.dom.value = d.activePage;
24854 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24855 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24858 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))
24860 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24861 this.field.dom.value = pageNum;
24862 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24865 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24867 var v = this.field.dom.value, pageNum;
24868 var increment = (e.shiftKey) ? 10 : 1;
24869 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24872 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24873 this.field.dom.value = d.activePage;
24876 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24878 this.field.dom.value = parseInt(v, 10) + increment;
24879 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24880 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24887 beforeLoad : function(){
24889 this.loading.disable();
24894 onClick : function(which){
24903 ds.load({params:{start: 0, limit: this.pageSize}});
24906 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24909 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24912 var total = ds.getTotalCount();
24913 var extra = total % this.pageSize;
24914 var lastStart = extra ? (total - extra) : total-this.pageSize;
24915 ds.load({params:{start: lastStart, limit: this.pageSize}});
24918 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24924 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24925 * @param {Roo.data.Store} store The data store to unbind
24927 unbind : function(ds){
24928 ds.un("beforeload", this.beforeLoad, this);
24929 ds.un("load", this.onLoad, this);
24930 ds.un("loadexception", this.onLoadError, this);
24931 ds.un("remove", this.updateInfo, this);
24932 ds.un("add", this.updateInfo, this);
24933 this.ds = undefined;
24937 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24938 * @param {Roo.data.Store} store The data store to bind
24940 bind : function(ds){
24941 ds.on("beforeload", this.beforeLoad, this);
24942 ds.on("load", this.onLoad, this);
24943 ds.on("loadexception", this.onLoadError, this);
24944 ds.on("remove", this.updateInfo, this);
24945 ds.on("add", this.updateInfo, this);
24956 * @class Roo.bootstrap.MessageBar
24957 * @extends Roo.bootstrap.Component
24958 * Bootstrap MessageBar class
24959 * @cfg {String} html contents of the MessageBar
24960 * @cfg {String} weight (info | success | warning | danger) default info
24961 * @cfg {String} beforeClass insert the bar before the given class
24962 * @cfg {Boolean} closable (true | false) default false
24963 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24966 * Create a new Element
24967 * @param {Object} config The config object
24970 Roo.bootstrap.MessageBar = function(config){
24971 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24974 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24980 beforeClass: 'bootstrap-sticky-wrap',
24982 getAutoCreate : function(){
24986 cls: 'alert alert-dismissable alert-' + this.weight,
24991 html: this.html || ''
24997 cfg.cls += ' alert-messages-fixed';
25011 onRender : function(ct, position)
25013 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25016 var cfg = Roo.apply({}, this.getAutoCreate());
25020 cfg.cls += ' ' + this.cls;
25023 cfg.style = this.style;
25025 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25027 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25030 this.el.select('>button.close').on('click', this.hide, this);
25036 if (!this.rendered) {
25042 this.fireEvent('show', this);
25048 if (!this.rendered) {
25054 this.fireEvent('hide', this);
25057 update : function()
25059 // var e = this.el.dom.firstChild;
25061 // if(this.closable){
25062 // e = e.nextSibling;
25065 // e.data = this.html || '';
25067 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25083 * @class Roo.bootstrap.Graph
25084 * @extends Roo.bootstrap.Component
25085 * Bootstrap Graph class
25089 @cfg {String} graphtype bar | vbar | pie
25090 @cfg {number} g_x coodinator | centre x (pie)
25091 @cfg {number} g_y coodinator | centre y (pie)
25092 @cfg {number} g_r radius (pie)
25093 @cfg {number} g_height height of the chart (respected by all elements in the set)
25094 @cfg {number} g_width width of the chart (respected by all elements in the set)
25095 @cfg {Object} title The title of the chart
25098 -opts (object) options for the chart
25100 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25101 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25103 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.
25104 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25106 o stretch (boolean)
25108 -opts (object) options for the pie
25111 o startAngle (number)
25112 o endAngle (number)
25116 * Create a new Input
25117 * @param {Object} config The config object
25120 Roo.bootstrap.Graph = function(config){
25121 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25127 * The img click event for the img.
25128 * @param {Roo.EventObject} e
25134 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25145 //g_colors: this.colors,
25152 getAutoCreate : function(){
25163 onRender : function(ct,position){
25166 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25168 if (typeof(Raphael) == 'undefined') {
25169 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25173 this.raphael = Raphael(this.el.dom);
25175 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25176 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25177 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25178 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25180 r.text(160, 10, "Single Series Chart").attr(txtattr);
25181 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25182 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25183 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25185 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25186 r.barchart(330, 10, 300, 220, data1);
25187 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25188 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25191 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25192 // r.barchart(30, 30, 560, 250, xdata, {
25193 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25194 // axis : "0 0 1 1",
25195 // axisxlabels : xdata
25196 // //yvalues : cols,
25199 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25201 // this.load(null,xdata,{
25202 // axis : "0 0 1 1",
25203 // axisxlabels : xdata
25208 load : function(graphtype,xdata,opts)
25210 this.raphael.clear();
25212 graphtype = this.graphtype;
25217 var r = this.raphael,
25218 fin = function () {
25219 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25221 fout = function () {
25222 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25224 pfin = function() {
25225 this.sector.stop();
25226 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25229 this.label[0].stop();
25230 this.label[0].attr({ r: 7.5 });
25231 this.label[1].attr({ "font-weight": 800 });
25234 pfout = function() {
25235 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25238 this.label[0].animate({ r: 5 }, 500, "bounce");
25239 this.label[1].attr({ "font-weight": 400 });
25245 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25248 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25251 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25252 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25254 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25261 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25266 setTitle: function(o)
25271 initEvents: function() {
25274 this.el.on('click', this.onClick, this);
25278 onClick : function(e)
25280 Roo.log('img onclick');
25281 this.fireEvent('click', this, e);
25293 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25296 * @class Roo.bootstrap.dash.NumberBox
25297 * @extends Roo.bootstrap.Component
25298 * Bootstrap NumberBox class
25299 * @cfg {String} headline Box headline
25300 * @cfg {String} content Box content
25301 * @cfg {String} icon Box icon
25302 * @cfg {String} footer Footer text
25303 * @cfg {String} fhref Footer href
25306 * Create a new NumberBox
25307 * @param {Object} config The config object
25311 Roo.bootstrap.dash.NumberBox = function(config){
25312 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25316 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25325 getAutoCreate : function(){
25329 cls : 'small-box ',
25337 cls : 'roo-headline',
25338 html : this.headline
25342 cls : 'roo-content',
25343 html : this.content
25357 cls : 'ion ' + this.icon
25366 cls : 'small-box-footer',
25367 href : this.fhref || '#',
25371 cfg.cn.push(footer);
25378 onRender : function(ct,position){
25379 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25386 setHeadline: function (value)
25388 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25391 setFooter: function (value, href)
25393 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25396 this.el.select('a.small-box-footer',true).first().attr('href', href);
25401 setContent: function (value)
25403 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25406 initEvents: function()
25420 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25423 * @class Roo.bootstrap.dash.TabBox
25424 * @extends Roo.bootstrap.Component
25425 * Bootstrap TabBox class
25426 * @cfg {String} title Title of the TabBox
25427 * @cfg {String} icon Icon of the TabBox
25428 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25429 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25432 * Create a new TabBox
25433 * @param {Object} config The config object
25437 Roo.bootstrap.dash.TabBox = function(config){
25438 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25443 * When a pane is added
25444 * @param {Roo.bootstrap.dash.TabPane} pane
25448 * @event activatepane
25449 * When a pane is activated
25450 * @param {Roo.bootstrap.dash.TabPane} pane
25452 "activatepane" : true
25460 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25465 tabScrollable : false,
25467 getChildContainer : function()
25469 return this.el.select('.tab-content', true).first();
25472 getAutoCreate : function(){
25476 cls: 'pull-left header',
25484 cls: 'fa ' + this.icon
25490 cls: 'nav nav-tabs pull-right',
25496 if(this.tabScrollable){
25503 cls: 'nav nav-tabs pull-right',
25514 cls: 'nav-tabs-custom',
25519 cls: 'tab-content no-padding',
25527 initEvents : function()
25529 //Roo.log('add add pane handler');
25530 this.on('addpane', this.onAddPane, this);
25533 * Updates the box title
25534 * @param {String} html to set the title to.
25536 setTitle : function(value)
25538 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25540 onAddPane : function(pane)
25542 this.panes.push(pane);
25543 //Roo.log('addpane');
25545 // tabs are rendere left to right..
25546 if(!this.showtabs){
25550 var ctr = this.el.select('.nav-tabs', true).first();
25553 var existing = ctr.select('.nav-tab',true);
25554 var qty = existing.getCount();;
25557 var tab = ctr.createChild({
25559 cls : 'nav-tab' + (qty ? '' : ' active'),
25567 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25570 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25572 pane.el.addClass('active');
25577 onTabClick : function(ev,un,ob,pane)
25579 //Roo.log('tab - prev default');
25580 ev.preventDefault();
25583 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25584 pane.tab.addClass('active');
25585 //Roo.log(pane.title);
25586 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25587 // technically we should have a deactivate event.. but maybe add later.
25588 // and it should not de-activate the selected tab...
25589 this.fireEvent('activatepane', pane);
25590 pane.el.addClass('active');
25591 pane.fireEvent('activate');
25596 getActivePane : function()
25599 Roo.each(this.panes, function(p) {
25600 if(p.el.hasClass('active')){
25621 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25623 * @class Roo.bootstrap.TabPane
25624 * @extends Roo.bootstrap.Component
25625 * Bootstrap TabPane class
25626 * @cfg {Boolean} active (false | true) Default false
25627 * @cfg {String} title title of panel
25631 * Create a new TabPane
25632 * @param {Object} config The config object
25635 Roo.bootstrap.dash.TabPane = function(config){
25636 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25642 * When a pane is activated
25643 * @param {Roo.bootstrap.dash.TabPane} pane
25650 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25655 // the tabBox that this is attached to.
25658 getAutoCreate : function()
25666 cfg.cls += ' active';
25671 initEvents : function()
25673 //Roo.log('trigger add pane handler');
25674 this.parent().fireEvent('addpane', this)
25678 * Updates the tab title
25679 * @param {String} html to set the title to.
25681 setTitle: function(str)
25687 this.tab.select('a', true).first().dom.innerHTML = str;
25704 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25707 * @class Roo.bootstrap.menu.Menu
25708 * @extends Roo.bootstrap.Component
25709 * Bootstrap Menu class - container for Menu
25710 * @cfg {String} html Text of the menu
25711 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25712 * @cfg {String} icon Font awesome icon
25713 * @cfg {String} pos Menu align to (top | bottom) default bottom
25717 * Create a new Menu
25718 * @param {Object} config The config object
25722 Roo.bootstrap.menu.Menu = function(config){
25723 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25727 * @event beforeshow
25728 * Fires before this menu is displayed
25729 * @param {Roo.bootstrap.menu.Menu} this
25733 * @event beforehide
25734 * Fires before this menu is hidden
25735 * @param {Roo.bootstrap.menu.Menu} this
25740 * Fires after this menu is displayed
25741 * @param {Roo.bootstrap.menu.Menu} this
25746 * Fires after this menu is hidden
25747 * @param {Roo.bootstrap.menu.Menu} this
25752 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25753 * @param {Roo.bootstrap.menu.Menu} this
25754 * @param {Roo.EventObject} e
25761 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25765 weight : 'default',
25770 getChildContainer : function() {
25771 if(this.isSubMenu){
25775 return this.el.select('ul.dropdown-menu', true).first();
25778 getAutoCreate : function()
25783 cls : 'roo-menu-text',
25791 cls : 'fa ' + this.icon
25802 cls : 'dropdown-button btn btn-' + this.weight,
25807 cls : 'dropdown-toggle btn btn-' + this.weight,
25817 cls : 'dropdown-menu'
25823 if(this.pos == 'top'){
25824 cfg.cls += ' dropup';
25827 if(this.isSubMenu){
25830 cls : 'dropdown-menu'
25837 onRender : function(ct, position)
25839 this.isSubMenu = ct.hasClass('dropdown-submenu');
25841 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25844 initEvents : function()
25846 if(this.isSubMenu){
25850 this.hidden = true;
25852 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25853 this.triggerEl.on('click', this.onTriggerPress, this);
25855 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25856 this.buttonEl.on('click', this.onClick, this);
25862 if(this.isSubMenu){
25866 return this.el.select('ul.dropdown-menu', true).first();
25869 onClick : function(e)
25871 this.fireEvent("click", this, e);
25874 onTriggerPress : function(e)
25876 if (this.isVisible()) {
25883 isVisible : function(){
25884 return !this.hidden;
25889 this.fireEvent("beforeshow", this);
25891 this.hidden = false;
25892 this.el.addClass('open');
25894 Roo.get(document).on("mouseup", this.onMouseUp, this);
25896 this.fireEvent("show", this);
25903 this.fireEvent("beforehide", this);
25905 this.hidden = true;
25906 this.el.removeClass('open');
25908 Roo.get(document).un("mouseup", this.onMouseUp);
25910 this.fireEvent("hide", this);
25913 onMouseUp : function()
25927 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25930 * @class Roo.bootstrap.menu.Item
25931 * @extends Roo.bootstrap.Component
25932 * Bootstrap MenuItem class
25933 * @cfg {Boolean} submenu (true | false) default false
25934 * @cfg {String} html text of the item
25935 * @cfg {String} href the link
25936 * @cfg {Boolean} disable (true | false) default false
25937 * @cfg {Boolean} preventDefault (true | false) default true
25938 * @cfg {String} icon Font awesome icon
25939 * @cfg {String} pos Submenu align to (left | right) default right
25943 * Create a new Item
25944 * @param {Object} config The config object
25948 Roo.bootstrap.menu.Item = function(config){
25949 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25953 * Fires when the mouse is hovering over this menu
25954 * @param {Roo.bootstrap.menu.Item} this
25955 * @param {Roo.EventObject} e
25960 * Fires when the mouse exits this menu
25961 * @param {Roo.bootstrap.menu.Item} this
25962 * @param {Roo.EventObject} e
25968 * The raw click event for the entire grid.
25969 * @param {Roo.EventObject} e
25975 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25980 preventDefault: true,
25985 getAutoCreate : function()
25990 cls : 'roo-menu-item-text',
25998 cls : 'fa ' + this.icon
26007 href : this.href || '#',
26014 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26018 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26020 if(this.pos == 'left'){
26021 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26028 initEvents : function()
26030 this.el.on('mouseover', this.onMouseOver, this);
26031 this.el.on('mouseout', this.onMouseOut, this);
26033 this.el.select('a', true).first().on('click', this.onClick, this);
26037 onClick : function(e)
26039 if(this.preventDefault){
26040 e.preventDefault();
26043 this.fireEvent("click", this, e);
26046 onMouseOver : function(e)
26048 if(this.submenu && this.pos == 'left'){
26049 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26052 this.fireEvent("mouseover", this, e);
26055 onMouseOut : function(e)
26057 this.fireEvent("mouseout", this, e);
26069 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26072 * @class Roo.bootstrap.menu.Separator
26073 * @extends Roo.bootstrap.Component
26074 * Bootstrap Separator class
26077 * Create a new Separator
26078 * @param {Object} config The config object
26082 Roo.bootstrap.menu.Separator = function(config){
26083 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26086 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26088 getAutoCreate : function(){
26109 * @class Roo.bootstrap.Tooltip
26110 * Bootstrap Tooltip class
26111 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26112 * to determine which dom element triggers the tooltip.
26114 * It needs to add support for additional attributes like tooltip-position
26117 * Create a new Toolti
26118 * @param {Object} config The config object
26121 Roo.bootstrap.Tooltip = function(config){
26122 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26124 this.alignment = Roo.bootstrap.Tooltip.alignment;
26126 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26127 this.alignment = config.alignment;
26132 Roo.apply(Roo.bootstrap.Tooltip, {
26134 * @function init initialize tooltip monitoring.
26138 currentTip : false,
26139 currentRegion : false,
26145 Roo.get(document).on('mouseover', this.enter ,this);
26146 Roo.get(document).on('mouseout', this.leave, this);
26149 this.currentTip = new Roo.bootstrap.Tooltip();
26152 enter : function(ev)
26154 var dom = ev.getTarget();
26156 //Roo.log(['enter',dom]);
26157 var el = Roo.fly(dom);
26158 if (this.currentEl) {
26160 //Roo.log(this.currentEl);
26161 //Roo.log(this.currentEl.contains(dom));
26162 if (this.currentEl == el) {
26165 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26171 if (this.currentTip.el) {
26172 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26176 if(!el || el.dom == document){
26182 // you can not look for children, as if el is the body.. then everythign is the child..
26183 if (!el.attr('tooltip')) { //
26184 if (!el.select("[tooltip]").elements.length) {
26187 // is the mouse over this child...?
26188 bindEl = el.select("[tooltip]").first();
26189 var xy = ev.getXY();
26190 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26191 //Roo.log("not in region.");
26194 //Roo.log("child element over..");
26197 this.currentEl = bindEl;
26198 this.currentTip.bind(bindEl);
26199 this.currentRegion = Roo.lib.Region.getRegion(dom);
26200 this.currentTip.enter();
26203 leave : function(ev)
26205 var dom = ev.getTarget();
26206 //Roo.log(['leave',dom]);
26207 if (!this.currentEl) {
26212 if (dom != this.currentEl.dom) {
26215 var xy = ev.getXY();
26216 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26219 // only activate leave if mouse cursor is outside... bounding box..
26224 if (this.currentTip) {
26225 this.currentTip.leave();
26227 //Roo.log('clear currentEl');
26228 this.currentEl = false;
26233 'left' : ['r-l', [-2,0], 'right'],
26234 'right' : ['l-r', [2,0], 'left'],
26235 'bottom' : ['t-b', [0,2], 'top'],
26236 'top' : [ 'b-t', [0,-2], 'bottom']
26242 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26247 delay : null, // can be { show : 300 , hide: 500}
26251 hoverState : null, //???
26253 placement : 'bottom',
26257 getAutoCreate : function(){
26264 cls : 'tooltip-arrow'
26267 cls : 'tooltip-inner'
26274 bind : function(el)
26280 enter : function () {
26282 if (this.timeout != null) {
26283 clearTimeout(this.timeout);
26286 this.hoverState = 'in';
26287 //Roo.log("enter - show");
26288 if (!this.delay || !this.delay.show) {
26293 this.timeout = setTimeout(function () {
26294 if (_t.hoverState == 'in') {
26297 }, this.delay.show);
26301 clearTimeout(this.timeout);
26303 this.hoverState = 'out';
26304 if (!this.delay || !this.delay.hide) {
26310 this.timeout = setTimeout(function () {
26311 //Roo.log("leave - timeout");
26313 if (_t.hoverState == 'out') {
26315 Roo.bootstrap.Tooltip.currentEl = false;
26320 show : function (msg)
26323 this.render(document.body);
26326 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26328 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26330 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26332 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26334 var placement = typeof this.placement == 'function' ?
26335 this.placement.call(this, this.el, on_el) :
26338 var autoToken = /\s?auto?\s?/i;
26339 var autoPlace = autoToken.test(placement);
26341 placement = placement.replace(autoToken, '') || 'top';
26345 //this.el.setXY([0,0]);
26347 //this.el.dom.style.display='block';
26349 //this.el.appendTo(on_el);
26351 var p = this.getPosition();
26352 var box = this.el.getBox();
26358 var align = this.alignment[placement];
26360 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26362 if(placement == 'top' || placement == 'bottom'){
26364 placement = 'right';
26367 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26368 placement = 'left';
26371 var scroll = Roo.select('body', true).first().getScroll();
26373 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26377 align = this.alignment[placement];
26380 this.el.alignTo(this.bindEl, align[0],align[1]);
26381 //var arrow = this.el.select('.arrow',true).first();
26382 //arrow.set(align[2],
26384 this.el.addClass(placement);
26386 this.el.addClass('in fade');
26388 this.hoverState = null;
26390 if (this.el.hasClass('fade')) {
26401 //this.el.setXY([0,0]);
26402 this.el.removeClass('in');
26418 * @class Roo.bootstrap.LocationPicker
26419 * @extends Roo.bootstrap.Component
26420 * Bootstrap LocationPicker class
26421 * @cfg {Number} latitude Position when init default 0
26422 * @cfg {Number} longitude Position when init default 0
26423 * @cfg {Number} zoom default 15
26424 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26425 * @cfg {Boolean} mapTypeControl default false
26426 * @cfg {Boolean} disableDoubleClickZoom default false
26427 * @cfg {Boolean} scrollwheel default true
26428 * @cfg {Boolean} streetViewControl default false
26429 * @cfg {Number} radius default 0
26430 * @cfg {String} locationName
26431 * @cfg {Boolean} draggable default true
26432 * @cfg {Boolean} enableAutocomplete default false
26433 * @cfg {Boolean} enableReverseGeocode default true
26434 * @cfg {String} markerTitle
26437 * Create a new LocationPicker
26438 * @param {Object} config The config object
26442 Roo.bootstrap.LocationPicker = function(config){
26444 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26449 * Fires when the picker initialized.
26450 * @param {Roo.bootstrap.LocationPicker} this
26451 * @param {Google Location} location
26455 * @event positionchanged
26456 * Fires when the picker position changed.
26457 * @param {Roo.bootstrap.LocationPicker} this
26458 * @param {Google Location} location
26460 positionchanged : true,
26463 * Fires when the map resize.
26464 * @param {Roo.bootstrap.LocationPicker} this
26469 * Fires when the map show.
26470 * @param {Roo.bootstrap.LocationPicker} this
26475 * Fires when the map hide.
26476 * @param {Roo.bootstrap.LocationPicker} this
26481 * Fires when click the map.
26482 * @param {Roo.bootstrap.LocationPicker} this
26483 * @param {Map event} e
26487 * @event mapRightClick
26488 * Fires when right click the map.
26489 * @param {Roo.bootstrap.LocationPicker} this
26490 * @param {Map event} e
26492 mapRightClick : true,
26494 * @event markerClick
26495 * Fires when click the marker.
26496 * @param {Roo.bootstrap.LocationPicker} this
26497 * @param {Map event} e
26499 markerClick : true,
26501 * @event markerRightClick
26502 * Fires when right click the marker.
26503 * @param {Roo.bootstrap.LocationPicker} this
26504 * @param {Map event} e
26506 markerRightClick : true,
26508 * @event OverlayViewDraw
26509 * Fires when OverlayView Draw
26510 * @param {Roo.bootstrap.LocationPicker} this
26512 OverlayViewDraw : true,
26514 * @event OverlayViewOnAdd
26515 * Fires when OverlayView Draw
26516 * @param {Roo.bootstrap.LocationPicker} this
26518 OverlayViewOnAdd : true,
26520 * @event OverlayViewOnRemove
26521 * Fires when OverlayView Draw
26522 * @param {Roo.bootstrap.LocationPicker} this
26524 OverlayViewOnRemove : true,
26526 * @event OverlayViewShow
26527 * Fires when OverlayView Draw
26528 * @param {Roo.bootstrap.LocationPicker} this
26529 * @param {Pixel} cpx
26531 OverlayViewShow : true,
26533 * @event OverlayViewHide
26534 * Fires when OverlayView Draw
26535 * @param {Roo.bootstrap.LocationPicker} this
26537 OverlayViewHide : true,
26539 * @event loadexception
26540 * Fires when load google lib failed.
26541 * @param {Roo.bootstrap.LocationPicker} this
26543 loadexception : true
26548 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26550 gMapContext: false,
26556 mapTypeControl: false,
26557 disableDoubleClickZoom: false,
26559 streetViewControl: false,
26563 enableAutocomplete: false,
26564 enableReverseGeocode: true,
26567 getAutoCreate: function()
26572 cls: 'roo-location-picker'
26578 initEvents: function(ct, position)
26580 if(!this.el.getWidth() || this.isApplied()){
26584 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26589 initial: function()
26591 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26592 this.fireEvent('loadexception', this);
26596 if(!this.mapTypeId){
26597 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26600 this.gMapContext = this.GMapContext();
26602 this.initOverlayView();
26604 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26608 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26609 _this.setPosition(_this.gMapContext.marker.position);
26612 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26613 _this.fireEvent('mapClick', this, event);
26617 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26618 _this.fireEvent('mapRightClick', this, event);
26622 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26623 _this.fireEvent('markerClick', this, event);
26627 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26628 _this.fireEvent('markerRightClick', this, event);
26632 this.setPosition(this.gMapContext.location);
26634 this.fireEvent('initial', this, this.gMapContext.location);
26637 initOverlayView: function()
26641 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26645 _this.fireEvent('OverlayViewDraw', _this);
26650 _this.fireEvent('OverlayViewOnAdd', _this);
26653 onRemove: function()
26655 _this.fireEvent('OverlayViewOnRemove', _this);
26658 show: function(cpx)
26660 _this.fireEvent('OverlayViewShow', _this, cpx);
26665 _this.fireEvent('OverlayViewHide', _this);
26671 fromLatLngToContainerPixel: function(event)
26673 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26676 isApplied: function()
26678 return this.getGmapContext() == false ? false : true;
26681 getGmapContext: function()
26683 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26686 GMapContext: function()
26688 var position = new google.maps.LatLng(this.latitude, this.longitude);
26690 var _map = new google.maps.Map(this.el.dom, {
26693 mapTypeId: this.mapTypeId,
26694 mapTypeControl: this.mapTypeControl,
26695 disableDoubleClickZoom: this.disableDoubleClickZoom,
26696 scrollwheel: this.scrollwheel,
26697 streetViewControl: this.streetViewControl,
26698 locationName: this.locationName,
26699 draggable: this.draggable,
26700 enableAutocomplete: this.enableAutocomplete,
26701 enableReverseGeocode: this.enableReverseGeocode
26704 var _marker = new google.maps.Marker({
26705 position: position,
26707 title: this.markerTitle,
26708 draggable: this.draggable
26715 location: position,
26716 radius: this.radius,
26717 locationName: this.locationName,
26718 addressComponents: {
26719 formatted_address: null,
26720 addressLine1: null,
26721 addressLine2: null,
26723 streetNumber: null,
26727 stateOrProvince: null
26730 domContainer: this.el.dom,
26731 geodecoder: new google.maps.Geocoder()
26735 drawCircle: function(center, radius, options)
26737 if (this.gMapContext.circle != null) {
26738 this.gMapContext.circle.setMap(null);
26742 options = Roo.apply({}, options, {
26743 strokeColor: "#0000FF",
26744 strokeOpacity: .35,
26746 fillColor: "#0000FF",
26750 options.map = this.gMapContext.map;
26751 options.radius = radius;
26752 options.center = center;
26753 this.gMapContext.circle = new google.maps.Circle(options);
26754 return this.gMapContext.circle;
26760 setPosition: function(location)
26762 this.gMapContext.location = location;
26763 this.gMapContext.marker.setPosition(location);
26764 this.gMapContext.map.panTo(location);
26765 this.drawCircle(location, this.gMapContext.radius, {});
26769 if (this.gMapContext.settings.enableReverseGeocode) {
26770 this.gMapContext.geodecoder.geocode({
26771 latLng: this.gMapContext.location
26772 }, function(results, status) {
26774 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26775 _this.gMapContext.locationName = results[0].formatted_address;
26776 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26778 _this.fireEvent('positionchanged', this, location);
26785 this.fireEvent('positionchanged', this, location);
26790 google.maps.event.trigger(this.gMapContext.map, "resize");
26792 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26794 this.fireEvent('resize', this);
26797 setPositionByLatLng: function(latitude, longitude)
26799 this.setPosition(new google.maps.LatLng(latitude, longitude));
26802 getCurrentPosition: function()
26805 latitude: this.gMapContext.location.lat(),
26806 longitude: this.gMapContext.location.lng()
26810 getAddressName: function()
26812 return this.gMapContext.locationName;
26815 getAddressComponents: function()
26817 return this.gMapContext.addressComponents;
26820 address_component_from_google_geocode: function(address_components)
26824 for (var i = 0; i < address_components.length; i++) {
26825 var component = address_components[i];
26826 if (component.types.indexOf("postal_code") >= 0) {
26827 result.postalCode = component.short_name;
26828 } else if (component.types.indexOf("street_number") >= 0) {
26829 result.streetNumber = component.short_name;
26830 } else if (component.types.indexOf("route") >= 0) {
26831 result.streetName = component.short_name;
26832 } else if (component.types.indexOf("neighborhood") >= 0) {
26833 result.city = component.short_name;
26834 } else if (component.types.indexOf("locality") >= 0) {
26835 result.city = component.short_name;
26836 } else if (component.types.indexOf("sublocality") >= 0) {
26837 result.district = component.short_name;
26838 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26839 result.stateOrProvince = component.short_name;
26840 } else if (component.types.indexOf("country") >= 0) {
26841 result.country = component.short_name;
26845 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26846 result.addressLine2 = "";
26850 setZoomLevel: function(zoom)
26852 this.gMapContext.map.setZoom(zoom);
26865 this.fireEvent('show', this);
26876 this.fireEvent('hide', this);
26881 Roo.apply(Roo.bootstrap.LocationPicker, {
26883 OverlayView : function(map, options)
26885 options = options || {};
26899 * @class Roo.bootstrap.Alert
26900 * @extends Roo.bootstrap.Component
26901 * Bootstrap Alert class
26902 * @cfg {String} title The title of alert
26903 * @cfg {String} html The content of alert
26904 * @cfg {String} weight ( success | info | warning | danger )
26905 * @cfg {String} faicon font-awesomeicon
26908 * Create a new alert
26909 * @param {Object} config The config object
26913 Roo.bootstrap.Alert = function(config){
26914 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26918 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26925 getAutoCreate : function()
26934 cls : 'roo-alert-icon'
26939 cls : 'roo-alert-title',
26944 cls : 'roo-alert-text',
26951 cfg.cn[0].cls += ' fa ' + this.faicon;
26955 cfg.cls += ' alert-' + this.weight;
26961 initEvents: function()
26963 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26966 setTitle : function(str)
26968 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26971 setText : function(str)
26973 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26976 setWeight : function(weight)
26979 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26982 this.weight = weight;
26984 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26987 setIcon : function(icon)
26990 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26993 this.faicon = icon;
26995 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27016 * @class Roo.bootstrap.UploadCropbox
27017 * @extends Roo.bootstrap.Component
27018 * Bootstrap UploadCropbox class
27019 * @cfg {String} emptyText show when image has been loaded
27020 * @cfg {String} rotateNotify show when image too small to rotate
27021 * @cfg {Number} errorTimeout default 3000
27022 * @cfg {Number} minWidth default 300
27023 * @cfg {Number} minHeight default 300
27024 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27025 * @cfg {Boolean} isDocument (true|false) default false
27026 * @cfg {String} url action url
27027 * @cfg {String} paramName default 'imageUpload'
27028 * @cfg {String} method default POST
27029 * @cfg {Boolean} loadMask (true|false) default true
27030 * @cfg {Boolean} loadingText default 'Loading...'
27033 * Create a new UploadCropbox
27034 * @param {Object} config The config object
27037 Roo.bootstrap.UploadCropbox = function(config){
27038 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27042 * @event beforeselectfile
27043 * Fire before select file
27044 * @param {Roo.bootstrap.UploadCropbox} this
27046 "beforeselectfile" : true,
27049 * Fire after initEvent
27050 * @param {Roo.bootstrap.UploadCropbox} this
27055 * Fire after initEvent
27056 * @param {Roo.bootstrap.UploadCropbox} this
27057 * @param {String} data
27062 * Fire when preparing the file data
27063 * @param {Roo.bootstrap.UploadCropbox} this
27064 * @param {Object} file
27069 * Fire when get exception
27070 * @param {Roo.bootstrap.UploadCropbox} this
27071 * @param {XMLHttpRequest} xhr
27073 "exception" : true,
27075 * @event beforeloadcanvas
27076 * Fire before load the canvas
27077 * @param {Roo.bootstrap.UploadCropbox} this
27078 * @param {String} src
27080 "beforeloadcanvas" : true,
27083 * Fire when trash image
27084 * @param {Roo.bootstrap.UploadCropbox} this
27089 * Fire when download the image
27090 * @param {Roo.bootstrap.UploadCropbox} this
27094 * @event footerbuttonclick
27095 * Fire when footerbuttonclick
27096 * @param {Roo.bootstrap.UploadCropbox} this
27097 * @param {String} type
27099 "footerbuttonclick" : true,
27103 * @param {Roo.bootstrap.UploadCropbox} this
27108 * Fire when rotate the image
27109 * @param {Roo.bootstrap.UploadCropbox} this
27110 * @param {String} pos
27115 * Fire when inspect the file
27116 * @param {Roo.bootstrap.UploadCropbox} this
27117 * @param {Object} file
27122 * Fire when xhr upload the file
27123 * @param {Roo.bootstrap.UploadCropbox} this
27124 * @param {Object} data
27129 * Fire when arrange the file data
27130 * @param {Roo.bootstrap.UploadCropbox} this
27131 * @param {Object} formData
27136 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27139 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27141 emptyText : 'Click to upload image',
27142 rotateNotify : 'Image is too small to rotate',
27143 errorTimeout : 3000,
27157 cropType : 'image/jpeg',
27159 canvasLoaded : false,
27160 isDocument : false,
27162 paramName : 'imageUpload',
27164 loadingText : 'Loading...',
27167 getAutoCreate : function()
27171 cls : 'roo-upload-cropbox',
27175 cls : 'roo-upload-cropbox-selector',
27180 cls : 'roo-upload-cropbox-body',
27181 style : 'cursor:pointer',
27185 cls : 'roo-upload-cropbox-preview'
27189 cls : 'roo-upload-cropbox-thumb'
27193 cls : 'roo-upload-cropbox-empty-notify',
27194 html : this.emptyText
27198 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27199 html : this.rotateNotify
27205 cls : 'roo-upload-cropbox-footer',
27208 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27218 onRender : function(ct, position)
27220 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27222 if (this.buttons.length) {
27224 Roo.each(this.buttons, function(bb) {
27226 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27228 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27234 this.maskEl = this.el;
27238 initEvents : function()
27240 this.urlAPI = (window.createObjectURL && window) ||
27241 (window.URL && URL.revokeObjectURL && URL) ||
27242 (window.webkitURL && webkitURL);
27244 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27245 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27247 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27248 this.selectorEl.hide();
27250 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27251 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27253 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27254 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27255 this.thumbEl.hide();
27257 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27258 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27260 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27261 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27262 this.errorEl.hide();
27264 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27265 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27266 this.footerEl.hide();
27268 this.setThumbBoxSize();
27274 this.fireEvent('initial', this);
27281 window.addEventListener("resize", function() { _this.resize(); } );
27283 this.bodyEl.on('click', this.beforeSelectFile, this);
27286 this.bodyEl.on('touchstart', this.onTouchStart, this);
27287 this.bodyEl.on('touchmove', this.onTouchMove, this);
27288 this.bodyEl.on('touchend', this.onTouchEnd, this);
27292 this.bodyEl.on('mousedown', this.onMouseDown, this);
27293 this.bodyEl.on('mousemove', this.onMouseMove, this);
27294 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27295 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27296 Roo.get(document).on('mouseup', this.onMouseUp, this);
27299 this.selectorEl.on('change', this.onFileSelected, this);
27305 this.baseScale = 1;
27307 this.baseRotate = 1;
27308 this.dragable = false;
27309 this.pinching = false;
27312 this.cropData = false;
27313 this.notifyEl.dom.innerHTML = this.emptyText;
27315 this.selectorEl.dom.value = '';
27319 resize : function()
27321 if(this.fireEvent('resize', this) != false){
27322 this.setThumbBoxPosition();
27323 this.setCanvasPosition();
27327 onFooterButtonClick : function(e, el, o, type)
27330 case 'rotate-left' :
27331 this.onRotateLeft(e);
27333 case 'rotate-right' :
27334 this.onRotateRight(e);
27337 this.beforeSelectFile(e);
27352 this.fireEvent('footerbuttonclick', this, type);
27355 beforeSelectFile : function(e)
27357 e.preventDefault();
27359 if(this.fireEvent('beforeselectfile', this) != false){
27360 this.selectorEl.dom.click();
27364 onFileSelected : function(e)
27366 e.preventDefault();
27368 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27372 var file = this.selectorEl.dom.files[0];
27374 if(this.fireEvent('inspect', this, file) != false){
27375 this.prepare(file);
27380 trash : function(e)
27382 this.fireEvent('trash', this);
27385 download : function(e)
27387 this.fireEvent('download', this);
27390 loadCanvas : function(src)
27392 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27396 this.imageEl = document.createElement('img');
27400 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27402 this.imageEl.src = src;
27406 onLoadCanvas : function()
27408 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27409 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27411 this.bodyEl.un('click', this.beforeSelectFile, this);
27413 this.notifyEl.hide();
27414 this.thumbEl.show();
27415 this.footerEl.show();
27417 this.baseRotateLevel();
27419 if(this.isDocument){
27420 this.setThumbBoxSize();
27423 this.setThumbBoxPosition();
27425 this.baseScaleLevel();
27431 this.canvasLoaded = true;
27434 this.maskEl.unmask();
27439 setCanvasPosition : function()
27441 if(!this.canvasEl){
27445 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27446 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27448 this.previewEl.setLeft(pw);
27449 this.previewEl.setTop(ph);
27453 onMouseDown : function(e)
27457 this.dragable = true;
27458 this.pinching = false;
27460 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27461 this.dragable = false;
27465 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27466 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27470 onMouseMove : function(e)
27474 if(!this.canvasLoaded){
27478 if (!this.dragable){
27482 var minX = Math.ceil(this.thumbEl.getLeft(true));
27483 var minY = Math.ceil(this.thumbEl.getTop(true));
27485 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27486 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27488 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27489 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27491 x = x - this.mouseX;
27492 y = y - this.mouseY;
27494 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27495 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27497 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27498 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27500 this.previewEl.setLeft(bgX);
27501 this.previewEl.setTop(bgY);
27503 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27504 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27507 onMouseUp : function(e)
27511 this.dragable = false;
27514 onMouseWheel : function(e)
27518 this.startScale = this.scale;
27520 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27522 if(!this.zoomable()){
27523 this.scale = this.startScale;
27532 zoomable : function()
27534 var minScale = this.thumbEl.getWidth() / this.minWidth;
27536 if(this.minWidth < this.minHeight){
27537 minScale = this.thumbEl.getHeight() / this.minHeight;
27540 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27541 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27545 (this.rotate == 0 || this.rotate == 180) &&
27547 width > this.imageEl.OriginWidth ||
27548 height > this.imageEl.OriginHeight ||
27549 (width < this.minWidth && height < this.minHeight)
27557 (this.rotate == 90 || this.rotate == 270) &&
27559 width > this.imageEl.OriginWidth ||
27560 height > this.imageEl.OriginHeight ||
27561 (width < this.minHeight && height < this.minWidth)
27568 !this.isDocument &&
27569 (this.rotate == 0 || this.rotate == 180) &&
27571 width < this.minWidth ||
27572 width > this.imageEl.OriginWidth ||
27573 height < this.minHeight ||
27574 height > this.imageEl.OriginHeight
27581 !this.isDocument &&
27582 (this.rotate == 90 || this.rotate == 270) &&
27584 width < this.minHeight ||
27585 width > this.imageEl.OriginWidth ||
27586 height < this.minWidth ||
27587 height > this.imageEl.OriginHeight
27597 onRotateLeft : function(e)
27599 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27601 var minScale = this.thumbEl.getWidth() / this.minWidth;
27603 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27604 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27606 this.startScale = this.scale;
27608 while (this.getScaleLevel() < minScale){
27610 this.scale = this.scale + 1;
27612 if(!this.zoomable()){
27617 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27618 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27623 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27630 this.scale = this.startScale;
27632 this.onRotateFail();
27637 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27639 if(this.isDocument){
27640 this.setThumbBoxSize();
27641 this.setThumbBoxPosition();
27642 this.setCanvasPosition();
27647 this.fireEvent('rotate', this, 'left');
27651 onRotateRight : function(e)
27653 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27655 var minScale = this.thumbEl.getWidth() / this.minWidth;
27657 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27658 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27660 this.startScale = this.scale;
27662 while (this.getScaleLevel() < minScale){
27664 this.scale = this.scale + 1;
27666 if(!this.zoomable()){
27671 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27672 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27677 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27684 this.scale = this.startScale;
27686 this.onRotateFail();
27691 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27693 if(this.isDocument){
27694 this.setThumbBoxSize();
27695 this.setThumbBoxPosition();
27696 this.setCanvasPosition();
27701 this.fireEvent('rotate', this, 'right');
27704 onRotateFail : function()
27706 this.errorEl.show(true);
27710 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27715 this.previewEl.dom.innerHTML = '';
27717 var canvasEl = document.createElement("canvas");
27719 var contextEl = canvasEl.getContext("2d");
27721 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27722 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27723 var center = this.imageEl.OriginWidth / 2;
27725 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27726 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27727 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27728 center = this.imageEl.OriginHeight / 2;
27731 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27733 contextEl.translate(center, center);
27734 contextEl.rotate(this.rotate * Math.PI / 180);
27736 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27738 this.canvasEl = document.createElement("canvas");
27740 this.contextEl = this.canvasEl.getContext("2d");
27742 switch (this.rotate) {
27745 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27746 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27748 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27753 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27754 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27756 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27757 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);
27761 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27766 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27767 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27769 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27770 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);
27774 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);
27779 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27780 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27782 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27783 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27787 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);
27794 this.previewEl.appendChild(this.canvasEl);
27796 this.setCanvasPosition();
27801 if(!this.canvasLoaded){
27805 var imageCanvas = document.createElement("canvas");
27807 var imageContext = imageCanvas.getContext("2d");
27809 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27810 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27812 var center = imageCanvas.width / 2;
27814 imageContext.translate(center, center);
27816 imageContext.rotate(this.rotate * Math.PI / 180);
27818 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27820 var canvas = document.createElement("canvas");
27822 var context = canvas.getContext("2d");
27824 canvas.width = this.minWidth;
27825 canvas.height = this.minHeight;
27827 switch (this.rotate) {
27830 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27831 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27833 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27834 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27836 var targetWidth = this.minWidth - 2 * x;
27837 var targetHeight = this.minHeight - 2 * y;
27841 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27842 scale = targetWidth / width;
27845 if(x > 0 && y == 0){
27846 scale = targetHeight / height;
27849 if(x > 0 && y > 0){
27850 scale = targetWidth / width;
27852 if(width < height){
27853 scale = targetHeight / height;
27857 context.scale(scale, scale);
27859 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27860 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27862 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27863 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27865 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27870 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27871 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27873 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27874 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27876 var targetWidth = this.minWidth - 2 * x;
27877 var targetHeight = this.minHeight - 2 * y;
27881 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27882 scale = targetWidth / width;
27885 if(x > 0 && y == 0){
27886 scale = targetHeight / height;
27889 if(x > 0 && y > 0){
27890 scale = targetWidth / width;
27892 if(width < height){
27893 scale = targetHeight / height;
27897 context.scale(scale, scale);
27899 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27900 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27902 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27903 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27905 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27907 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27912 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27913 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27915 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27916 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27918 var targetWidth = this.minWidth - 2 * x;
27919 var targetHeight = this.minHeight - 2 * y;
27923 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27924 scale = targetWidth / width;
27927 if(x > 0 && y == 0){
27928 scale = targetHeight / height;
27931 if(x > 0 && y > 0){
27932 scale = targetWidth / width;
27934 if(width < height){
27935 scale = targetHeight / height;
27939 context.scale(scale, scale);
27941 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27942 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27944 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27945 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27947 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27948 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27950 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27955 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27956 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27958 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27959 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27961 var targetWidth = this.minWidth - 2 * x;
27962 var targetHeight = this.minHeight - 2 * y;
27966 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27967 scale = targetWidth / width;
27970 if(x > 0 && y == 0){
27971 scale = targetHeight / height;
27974 if(x > 0 && y > 0){
27975 scale = targetWidth / width;
27977 if(width < height){
27978 scale = targetHeight / height;
27982 context.scale(scale, scale);
27984 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27985 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27987 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27988 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27990 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27992 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27999 this.cropData = canvas.toDataURL(this.cropType);
28001 if(this.fireEvent('crop', this, this.cropData) !== false){
28002 this.process(this.file, this.cropData);
28009 setThumbBoxSize : function()
28013 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28014 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28015 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28017 this.minWidth = width;
28018 this.minHeight = height;
28020 if(this.rotate == 90 || this.rotate == 270){
28021 this.minWidth = height;
28022 this.minHeight = width;
28027 width = Math.ceil(this.minWidth * height / this.minHeight);
28029 if(this.minWidth > this.minHeight){
28031 height = Math.ceil(this.minHeight * width / this.minWidth);
28034 this.thumbEl.setStyle({
28035 width : width + 'px',
28036 height : height + 'px'
28043 setThumbBoxPosition : function()
28045 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28046 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28048 this.thumbEl.setLeft(x);
28049 this.thumbEl.setTop(y);
28053 baseRotateLevel : function()
28055 this.baseRotate = 1;
28058 typeof(this.exif) != 'undefined' &&
28059 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28060 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28062 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28065 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28069 baseScaleLevel : function()
28073 if(this.isDocument){
28075 if(this.baseRotate == 6 || this.baseRotate == 8){
28077 height = this.thumbEl.getHeight();
28078 this.baseScale = height / this.imageEl.OriginWidth;
28080 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28081 width = this.thumbEl.getWidth();
28082 this.baseScale = width / this.imageEl.OriginHeight;
28088 height = this.thumbEl.getHeight();
28089 this.baseScale = height / this.imageEl.OriginHeight;
28091 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28092 width = this.thumbEl.getWidth();
28093 this.baseScale = width / this.imageEl.OriginWidth;
28099 if(this.baseRotate == 6 || this.baseRotate == 8){
28101 width = this.thumbEl.getHeight();
28102 this.baseScale = width / this.imageEl.OriginHeight;
28104 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28105 height = this.thumbEl.getWidth();
28106 this.baseScale = height / this.imageEl.OriginHeight;
28109 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28110 height = this.thumbEl.getWidth();
28111 this.baseScale = height / this.imageEl.OriginHeight;
28113 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28114 width = this.thumbEl.getHeight();
28115 this.baseScale = width / this.imageEl.OriginWidth;
28122 width = this.thumbEl.getWidth();
28123 this.baseScale = width / this.imageEl.OriginWidth;
28125 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28126 height = this.thumbEl.getHeight();
28127 this.baseScale = height / this.imageEl.OriginHeight;
28130 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28132 height = this.thumbEl.getHeight();
28133 this.baseScale = height / this.imageEl.OriginHeight;
28135 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28136 width = this.thumbEl.getWidth();
28137 this.baseScale = width / this.imageEl.OriginWidth;
28145 getScaleLevel : function()
28147 return this.baseScale * Math.pow(1.1, this.scale);
28150 onTouchStart : function(e)
28152 if(!this.canvasLoaded){
28153 this.beforeSelectFile(e);
28157 var touches = e.browserEvent.touches;
28163 if(touches.length == 1){
28164 this.onMouseDown(e);
28168 if(touches.length != 2){
28174 for(var i = 0, finger; finger = touches[i]; i++){
28175 coords.push(finger.pageX, finger.pageY);
28178 var x = Math.pow(coords[0] - coords[2], 2);
28179 var y = Math.pow(coords[1] - coords[3], 2);
28181 this.startDistance = Math.sqrt(x + y);
28183 this.startScale = this.scale;
28185 this.pinching = true;
28186 this.dragable = false;
28190 onTouchMove : function(e)
28192 if(!this.pinching && !this.dragable){
28196 var touches = e.browserEvent.touches;
28203 this.onMouseMove(e);
28209 for(var i = 0, finger; finger = touches[i]; i++){
28210 coords.push(finger.pageX, finger.pageY);
28213 var x = Math.pow(coords[0] - coords[2], 2);
28214 var y = Math.pow(coords[1] - coords[3], 2);
28216 this.endDistance = Math.sqrt(x + y);
28218 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28220 if(!this.zoomable()){
28221 this.scale = this.startScale;
28229 onTouchEnd : function(e)
28231 this.pinching = false;
28232 this.dragable = false;
28236 process : function(file, crop)
28239 this.maskEl.mask(this.loadingText);
28242 this.xhr = new XMLHttpRequest();
28244 file.xhr = this.xhr;
28246 this.xhr.open(this.method, this.url, true);
28249 "Accept": "application/json",
28250 "Cache-Control": "no-cache",
28251 "X-Requested-With": "XMLHttpRequest"
28254 for (var headerName in headers) {
28255 var headerValue = headers[headerName];
28257 this.xhr.setRequestHeader(headerName, headerValue);
28263 this.xhr.onload = function()
28265 _this.xhrOnLoad(_this.xhr);
28268 this.xhr.onerror = function()
28270 _this.xhrOnError(_this.xhr);
28273 var formData = new FormData();
28275 formData.append('returnHTML', 'NO');
28278 formData.append('crop', crop);
28281 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28282 formData.append(this.paramName, file, file.name);
28285 if(typeof(file.filename) != 'undefined'){
28286 formData.append('filename', file.filename);
28289 if(typeof(file.mimetype) != 'undefined'){
28290 formData.append('mimetype', file.mimetype);
28293 if(this.fireEvent('arrange', this, formData) != false){
28294 this.xhr.send(formData);
28298 xhrOnLoad : function(xhr)
28301 this.maskEl.unmask();
28304 if (xhr.readyState !== 4) {
28305 this.fireEvent('exception', this, xhr);
28309 var response = Roo.decode(xhr.responseText);
28311 if(!response.success){
28312 this.fireEvent('exception', this, xhr);
28316 var response = Roo.decode(xhr.responseText);
28318 this.fireEvent('upload', this, response);
28322 xhrOnError : function()
28325 this.maskEl.unmask();
28328 Roo.log('xhr on error');
28330 var response = Roo.decode(xhr.responseText);
28336 prepare : function(file)
28339 this.maskEl.mask(this.loadingText);
28345 if(typeof(file) === 'string'){
28346 this.loadCanvas(file);
28350 if(!file || !this.urlAPI){
28355 this.cropType = file.type;
28359 if(this.fireEvent('prepare', this, this.file) != false){
28361 var reader = new FileReader();
28363 reader.onload = function (e) {
28364 if (e.target.error) {
28365 Roo.log(e.target.error);
28369 var buffer = e.target.result,
28370 dataView = new DataView(buffer),
28372 maxOffset = dataView.byteLength - 4,
28376 if (dataView.getUint16(0) === 0xffd8) {
28377 while (offset < maxOffset) {
28378 markerBytes = dataView.getUint16(offset);
28380 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28381 markerLength = dataView.getUint16(offset + 2) + 2;
28382 if (offset + markerLength > dataView.byteLength) {
28383 Roo.log('Invalid meta data: Invalid segment size.');
28387 if(markerBytes == 0xffe1){
28388 _this.parseExifData(
28395 offset += markerLength;
28405 var url = _this.urlAPI.createObjectURL(_this.file);
28407 _this.loadCanvas(url);
28412 reader.readAsArrayBuffer(this.file);
28418 parseExifData : function(dataView, offset, length)
28420 var tiffOffset = offset + 10,
28424 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28425 // No Exif data, might be XMP data instead
28429 // Check for the ASCII code for "Exif" (0x45786966):
28430 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28431 // No Exif data, might be XMP data instead
28434 if (tiffOffset + 8 > dataView.byteLength) {
28435 Roo.log('Invalid Exif data: Invalid segment size.');
28438 // Check for the two null bytes:
28439 if (dataView.getUint16(offset + 8) !== 0x0000) {
28440 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28443 // Check the byte alignment:
28444 switch (dataView.getUint16(tiffOffset)) {
28446 littleEndian = true;
28449 littleEndian = false;
28452 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28455 // Check for the TIFF tag marker (0x002A):
28456 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28457 Roo.log('Invalid Exif data: Missing TIFF marker.');
28460 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28461 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28463 this.parseExifTags(
28466 tiffOffset + dirOffset,
28471 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28476 if (dirOffset + 6 > dataView.byteLength) {
28477 Roo.log('Invalid Exif data: Invalid directory offset.');
28480 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28481 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28482 if (dirEndOffset + 4 > dataView.byteLength) {
28483 Roo.log('Invalid Exif data: Invalid directory size.');
28486 for (i = 0; i < tagsNumber; i += 1) {
28490 dirOffset + 2 + 12 * i, // tag offset
28494 // Return the offset to the next directory:
28495 return dataView.getUint32(dirEndOffset, littleEndian);
28498 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28500 var tag = dataView.getUint16(offset, littleEndian);
28502 this.exif[tag] = this.getExifValue(
28506 dataView.getUint16(offset + 2, littleEndian), // tag type
28507 dataView.getUint32(offset + 4, littleEndian), // tag length
28512 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28514 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28523 Roo.log('Invalid Exif data: Invalid tag type.');
28527 tagSize = tagType.size * length;
28528 // Determine if the value is contained in the dataOffset bytes,
28529 // or if the value at the dataOffset is a pointer to the actual data:
28530 dataOffset = tagSize > 4 ?
28531 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28532 if (dataOffset + tagSize > dataView.byteLength) {
28533 Roo.log('Invalid Exif data: Invalid data offset.');
28536 if (length === 1) {
28537 return tagType.getValue(dataView, dataOffset, littleEndian);
28540 for (i = 0; i < length; i += 1) {
28541 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28544 if (tagType.ascii) {
28546 // Concatenate the chars:
28547 for (i = 0; i < values.length; i += 1) {
28549 // Ignore the terminating NULL byte(s):
28550 if (c === '\u0000') {
28562 Roo.apply(Roo.bootstrap.UploadCropbox, {
28564 'Orientation': 0x0112
28568 1: 0, //'top-left',
28570 3: 180, //'bottom-right',
28571 // 4: 'bottom-left',
28573 6: 90, //'right-top',
28574 // 7: 'right-bottom',
28575 8: 270 //'left-bottom'
28579 // byte, 8-bit unsigned int:
28581 getValue: function (dataView, dataOffset) {
28582 return dataView.getUint8(dataOffset);
28586 // ascii, 8-bit byte:
28588 getValue: function (dataView, dataOffset) {
28589 return String.fromCharCode(dataView.getUint8(dataOffset));
28594 // short, 16 bit int:
28596 getValue: function (dataView, dataOffset, littleEndian) {
28597 return dataView.getUint16(dataOffset, littleEndian);
28601 // long, 32 bit int:
28603 getValue: function (dataView, dataOffset, littleEndian) {
28604 return dataView.getUint32(dataOffset, littleEndian);
28608 // rational = two long values, first is numerator, second is denominator:
28610 getValue: function (dataView, dataOffset, littleEndian) {
28611 return dataView.getUint32(dataOffset, littleEndian) /
28612 dataView.getUint32(dataOffset + 4, littleEndian);
28616 // slong, 32 bit signed int:
28618 getValue: function (dataView, dataOffset, littleEndian) {
28619 return dataView.getInt32(dataOffset, littleEndian);
28623 // srational, two slongs, first is numerator, second is denominator:
28625 getValue: function (dataView, dataOffset, littleEndian) {
28626 return dataView.getInt32(dataOffset, littleEndian) /
28627 dataView.getInt32(dataOffset + 4, littleEndian);
28637 cls : 'btn-group roo-upload-cropbox-rotate-left',
28638 action : 'rotate-left',
28642 cls : 'btn btn-default',
28643 html : '<i class="fa fa-undo"></i>'
28649 cls : 'btn-group roo-upload-cropbox-picture',
28650 action : 'picture',
28654 cls : 'btn btn-default',
28655 html : '<i class="fa fa-picture-o"></i>'
28661 cls : 'btn-group roo-upload-cropbox-rotate-right',
28662 action : 'rotate-right',
28666 cls : 'btn btn-default',
28667 html : '<i class="fa fa-repeat"></i>'
28675 cls : 'btn-group roo-upload-cropbox-rotate-left',
28676 action : 'rotate-left',
28680 cls : 'btn btn-default',
28681 html : '<i class="fa fa-undo"></i>'
28687 cls : 'btn-group roo-upload-cropbox-download',
28688 action : 'download',
28692 cls : 'btn btn-default',
28693 html : '<i class="fa fa-download"></i>'
28699 cls : 'btn-group roo-upload-cropbox-crop',
28704 cls : 'btn btn-default',
28705 html : '<i class="fa fa-crop"></i>'
28711 cls : 'btn-group roo-upload-cropbox-trash',
28716 cls : 'btn btn-default',
28717 html : '<i class="fa fa-trash"></i>'
28723 cls : 'btn-group roo-upload-cropbox-rotate-right',
28724 action : 'rotate-right',
28728 cls : 'btn btn-default',
28729 html : '<i class="fa fa-repeat"></i>'
28737 cls : 'btn-group roo-upload-cropbox-rotate-left',
28738 action : 'rotate-left',
28742 cls : 'btn btn-default',
28743 html : '<i class="fa fa-undo"></i>'
28749 cls : 'btn-group roo-upload-cropbox-rotate-right',
28750 action : 'rotate-right',
28754 cls : 'btn btn-default',
28755 html : '<i class="fa fa-repeat"></i>'
28768 * @class Roo.bootstrap.DocumentManager
28769 * @extends Roo.bootstrap.Component
28770 * Bootstrap DocumentManager class
28771 * @cfg {String} paramName default 'imageUpload'
28772 * @cfg {String} toolTipName default 'filename'
28773 * @cfg {String} method default POST
28774 * @cfg {String} url action url
28775 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28776 * @cfg {Boolean} multiple multiple upload default true
28777 * @cfg {Number} thumbSize default 300
28778 * @cfg {String} fieldLabel
28779 * @cfg {Number} labelWidth default 4
28780 * @cfg {String} labelAlign (left|top) default left
28781 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28782 * @cfg {Number} labellg set the width of label (1-12)
28783 * @cfg {Number} labelmd set the width of label (1-12)
28784 * @cfg {Number} labelsm set the width of label (1-12)
28785 * @cfg {Number} labelxs set the width of label (1-12)
28788 * Create a new DocumentManager
28789 * @param {Object} config The config object
28792 Roo.bootstrap.DocumentManager = function(config){
28793 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28796 this.delegates = [];
28801 * Fire when initial the DocumentManager
28802 * @param {Roo.bootstrap.DocumentManager} this
28807 * inspect selected file
28808 * @param {Roo.bootstrap.DocumentManager} this
28809 * @param {File} file
28814 * Fire when xhr load exception
28815 * @param {Roo.bootstrap.DocumentManager} this
28816 * @param {XMLHttpRequest} xhr
28818 "exception" : true,
28820 * @event afterupload
28821 * Fire when xhr load exception
28822 * @param {Roo.bootstrap.DocumentManager} this
28823 * @param {XMLHttpRequest} xhr
28825 "afterupload" : true,
28828 * prepare the form data
28829 * @param {Roo.bootstrap.DocumentManager} this
28830 * @param {Object} formData
28835 * Fire when remove the file
28836 * @param {Roo.bootstrap.DocumentManager} this
28837 * @param {Object} file
28842 * Fire after refresh the file
28843 * @param {Roo.bootstrap.DocumentManager} this
28848 * Fire after click the image
28849 * @param {Roo.bootstrap.DocumentManager} this
28850 * @param {Object} file
28855 * Fire when upload a image and editable set to true
28856 * @param {Roo.bootstrap.DocumentManager} this
28857 * @param {Object} file
28861 * @event beforeselectfile
28862 * Fire before select file
28863 * @param {Roo.bootstrap.DocumentManager} this
28865 "beforeselectfile" : true,
28868 * Fire before process file
28869 * @param {Roo.bootstrap.DocumentManager} this
28870 * @param {Object} file
28874 * @event previewrendered
28875 * Fire when preview rendered
28876 * @param {Roo.bootstrap.DocumentManager} this
28877 * @param {Object} file
28879 "previewrendered" : true,
28882 "previewResize" : true
28887 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28896 paramName : 'imageUpload',
28897 toolTipName : 'filename',
28900 labelAlign : 'left',
28910 getAutoCreate : function()
28912 var managerWidget = {
28914 cls : 'roo-document-manager',
28918 cls : 'roo-document-manager-selector',
28923 cls : 'roo-document-manager-uploader',
28927 cls : 'roo-document-manager-upload-btn',
28928 html : '<i class="fa fa-plus"></i>'
28939 cls : 'column col-md-12',
28944 if(this.fieldLabel.length){
28949 cls : 'column col-md-12',
28950 html : this.fieldLabel
28954 cls : 'column col-md-12',
28959 if(this.labelAlign == 'left'){
28964 html : this.fieldLabel
28973 if(this.labelWidth > 12){
28974 content[0].style = "width: " + this.labelWidth + 'px';
28977 if(this.labelWidth < 13 && this.labelmd == 0){
28978 this.labelmd = this.labelWidth;
28981 if(this.labellg > 0){
28982 content[0].cls += ' col-lg-' + this.labellg;
28983 content[1].cls += ' col-lg-' + (12 - this.labellg);
28986 if(this.labelmd > 0){
28987 content[0].cls += ' col-md-' + this.labelmd;
28988 content[1].cls += ' col-md-' + (12 - this.labelmd);
28991 if(this.labelsm > 0){
28992 content[0].cls += ' col-sm-' + this.labelsm;
28993 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28996 if(this.labelxs > 0){
28997 content[0].cls += ' col-xs-' + this.labelxs;
28998 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29006 cls : 'row clearfix',
29014 initEvents : function()
29016 this.managerEl = this.el.select('.roo-document-manager', true).first();
29017 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29019 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29020 this.selectorEl.hide();
29023 this.selectorEl.attr('multiple', 'multiple');
29026 this.selectorEl.on('change', this.onFileSelected, this);
29028 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29029 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29031 this.uploader.on('click', this.onUploaderClick, this);
29033 this.renderProgressDialog();
29037 window.addEventListener("resize", function() { _this.refresh(); } );
29039 this.fireEvent('initial', this);
29042 renderProgressDialog : function()
29046 this.progressDialog = new Roo.bootstrap.Modal({
29047 cls : 'roo-document-manager-progress-dialog',
29048 allow_close : false,
29058 btnclick : function() {
29059 _this.uploadCancel();
29065 this.progressDialog.render(Roo.get(document.body));
29067 this.progress = new Roo.bootstrap.Progress({
29068 cls : 'roo-document-manager-progress',
29073 this.progress.render(this.progressDialog.getChildContainer());
29075 this.progressBar = new Roo.bootstrap.ProgressBar({
29076 cls : 'roo-document-manager-progress-bar',
29079 aria_valuemax : 12,
29083 this.progressBar.render(this.progress.getChildContainer());
29086 onUploaderClick : function(e)
29088 e.preventDefault();
29090 if(this.fireEvent('beforeselectfile', this) != false){
29091 this.selectorEl.dom.click();
29096 onFileSelected : function(e)
29098 e.preventDefault();
29100 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29104 Roo.each(this.selectorEl.dom.files, function(file){
29105 if(this.fireEvent('inspect', this, file) != false){
29106 this.files.push(file);
29116 this.selectorEl.dom.value = '';
29118 if(!this.files || !this.files.length){
29122 if(this.boxes > 0 && this.files.length > this.boxes){
29123 this.files = this.files.slice(0, this.boxes);
29126 this.uploader.show();
29128 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29129 this.uploader.hide();
29138 Roo.each(this.files, function(file){
29140 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29141 var f = this.renderPreview(file);
29146 if(file.type.indexOf('image') != -1){
29147 this.delegates.push(
29149 _this.process(file);
29150 }).createDelegate(this)
29158 _this.process(file);
29159 }).createDelegate(this)
29164 this.files = files;
29166 this.delegates = this.delegates.concat(docs);
29168 if(!this.delegates.length){
29173 this.progressBar.aria_valuemax = this.delegates.length;
29180 arrange : function()
29182 if(!this.delegates.length){
29183 this.progressDialog.hide();
29188 var delegate = this.delegates.shift();
29190 this.progressDialog.show();
29192 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29194 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29199 refresh : function()
29201 this.uploader.show();
29203 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29204 this.uploader.hide();
29207 Roo.isTouch ? this.closable(false) : this.closable(true);
29209 this.fireEvent('refresh', this);
29212 onRemove : function(e, el, o)
29214 e.preventDefault();
29216 this.fireEvent('remove', this, o);
29220 remove : function(o)
29224 Roo.each(this.files, function(file){
29225 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29234 this.files = files;
29241 Roo.each(this.files, function(file){
29246 file.target.remove();
29255 onClick : function(e, el, o)
29257 e.preventDefault();
29259 this.fireEvent('click', this, o);
29263 closable : function(closable)
29265 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29267 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29279 xhrOnLoad : function(xhr)
29281 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29285 if (xhr.readyState !== 4) {
29287 this.fireEvent('exception', this, xhr);
29291 var response = Roo.decode(xhr.responseText);
29293 if(!response.success){
29295 this.fireEvent('exception', this, xhr);
29299 var file = this.renderPreview(response.data);
29301 this.files.push(file);
29305 this.fireEvent('afterupload', this, xhr);
29309 xhrOnError : function(xhr)
29311 Roo.log('xhr on error');
29313 var response = Roo.decode(xhr.responseText);
29320 process : function(file)
29322 if(this.fireEvent('process', this, file) !== false){
29323 if(this.editable && file.type.indexOf('image') != -1){
29324 this.fireEvent('edit', this, file);
29328 this.uploadStart(file, false);
29335 uploadStart : function(file, crop)
29337 this.xhr = new XMLHttpRequest();
29339 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29344 file.xhr = this.xhr;
29346 this.managerEl.createChild({
29348 cls : 'roo-document-manager-loading',
29352 tooltip : file.name,
29353 cls : 'roo-document-manager-thumb',
29354 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29360 this.xhr.open(this.method, this.url, true);
29363 "Accept": "application/json",
29364 "Cache-Control": "no-cache",
29365 "X-Requested-With": "XMLHttpRequest"
29368 for (var headerName in headers) {
29369 var headerValue = headers[headerName];
29371 this.xhr.setRequestHeader(headerName, headerValue);
29377 this.xhr.onload = function()
29379 _this.xhrOnLoad(_this.xhr);
29382 this.xhr.onerror = function()
29384 _this.xhrOnError(_this.xhr);
29387 var formData = new FormData();
29389 formData.append('returnHTML', 'NO');
29392 formData.append('crop', crop);
29395 formData.append(this.paramName, file, file.name);
29402 if(this.fireEvent('prepare', this, formData, options) != false){
29404 if(options.manually){
29408 this.xhr.send(formData);
29412 this.uploadCancel();
29415 uploadCancel : function()
29421 this.delegates = [];
29423 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29430 renderPreview : function(file)
29432 if(typeof(file.target) != 'undefined' && file.target){
29436 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29438 var previewEl = this.managerEl.createChild({
29440 cls : 'roo-document-manager-preview',
29444 tooltip : file[this.toolTipName],
29445 cls : 'roo-document-manager-thumb',
29446 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29451 html : '<i class="fa fa-times-circle"></i>'
29456 var close = previewEl.select('button.close', true).first();
29458 close.on('click', this.onRemove, this, file);
29460 file.target = previewEl;
29462 var image = previewEl.select('img', true).first();
29466 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29468 image.on('click', this.onClick, this, file);
29470 this.fireEvent('previewrendered', this, file);
29476 onPreviewLoad : function(file, image)
29478 if(typeof(file.target) == 'undefined' || !file.target){
29482 var width = image.dom.naturalWidth || image.dom.width;
29483 var height = image.dom.naturalHeight || image.dom.height;
29485 if(!this.previewResize) {
29489 if(width > height){
29490 file.target.addClass('wide');
29494 file.target.addClass('tall');
29499 uploadFromSource : function(file, crop)
29501 this.xhr = new XMLHttpRequest();
29503 this.managerEl.createChild({
29505 cls : 'roo-document-manager-loading',
29509 tooltip : file.name,
29510 cls : 'roo-document-manager-thumb',
29511 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29517 this.xhr.open(this.method, this.url, true);
29520 "Accept": "application/json",
29521 "Cache-Control": "no-cache",
29522 "X-Requested-With": "XMLHttpRequest"
29525 for (var headerName in headers) {
29526 var headerValue = headers[headerName];
29528 this.xhr.setRequestHeader(headerName, headerValue);
29534 this.xhr.onload = function()
29536 _this.xhrOnLoad(_this.xhr);
29539 this.xhr.onerror = function()
29541 _this.xhrOnError(_this.xhr);
29544 var formData = new FormData();
29546 formData.append('returnHTML', 'NO');
29548 formData.append('crop', crop);
29550 if(typeof(file.filename) != 'undefined'){
29551 formData.append('filename', file.filename);
29554 if(typeof(file.mimetype) != 'undefined'){
29555 formData.append('mimetype', file.mimetype);
29560 if(this.fireEvent('prepare', this, formData) != false){
29561 this.xhr.send(formData);
29571 * @class Roo.bootstrap.DocumentViewer
29572 * @extends Roo.bootstrap.Component
29573 * Bootstrap DocumentViewer class
29574 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29575 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29578 * Create a new DocumentViewer
29579 * @param {Object} config The config object
29582 Roo.bootstrap.DocumentViewer = function(config){
29583 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29588 * Fire after initEvent
29589 * @param {Roo.bootstrap.DocumentViewer} this
29595 * @param {Roo.bootstrap.DocumentViewer} this
29600 * Fire after download button
29601 * @param {Roo.bootstrap.DocumentViewer} this
29606 * Fire after trash button
29607 * @param {Roo.bootstrap.DocumentViewer} this
29614 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29616 showDownload : true,
29620 getAutoCreate : function()
29624 cls : 'roo-document-viewer',
29628 cls : 'roo-document-viewer-body',
29632 cls : 'roo-document-viewer-thumb',
29636 cls : 'roo-document-viewer-image'
29644 cls : 'roo-document-viewer-footer',
29647 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29651 cls : 'btn-group roo-document-viewer-download',
29655 cls : 'btn btn-default',
29656 html : '<i class="fa fa-download"></i>'
29662 cls : 'btn-group roo-document-viewer-trash',
29666 cls : 'btn btn-default',
29667 html : '<i class="fa fa-trash"></i>'
29680 initEvents : function()
29682 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29683 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29685 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29686 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29688 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29689 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29691 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29692 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29694 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29695 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29697 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29698 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29700 this.bodyEl.on('click', this.onClick, this);
29701 this.downloadBtn.on('click', this.onDownload, this);
29702 this.trashBtn.on('click', this.onTrash, this);
29704 this.downloadBtn.hide();
29705 this.trashBtn.hide();
29707 if(this.showDownload){
29708 this.downloadBtn.show();
29711 if(this.showTrash){
29712 this.trashBtn.show();
29715 if(!this.showDownload && !this.showTrash) {
29716 this.footerEl.hide();
29721 initial : function()
29723 this.fireEvent('initial', this);
29727 onClick : function(e)
29729 e.preventDefault();
29731 this.fireEvent('click', this);
29734 onDownload : function(e)
29736 e.preventDefault();
29738 this.fireEvent('download', this);
29741 onTrash : function(e)
29743 e.preventDefault();
29745 this.fireEvent('trash', this);
29757 * @class Roo.bootstrap.NavProgressBar
29758 * @extends Roo.bootstrap.Component
29759 * Bootstrap NavProgressBar class
29762 * Create a new nav progress bar
29763 * @param {Object} config The config object
29766 Roo.bootstrap.NavProgressBar = function(config){
29767 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29769 this.bullets = this.bullets || [];
29771 // Roo.bootstrap.NavProgressBar.register(this);
29775 * Fires when the active item changes
29776 * @param {Roo.bootstrap.NavProgressBar} this
29777 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29778 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29785 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29790 getAutoCreate : function()
29792 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29796 cls : 'roo-navigation-bar-group',
29800 cls : 'roo-navigation-top-bar'
29804 cls : 'roo-navigation-bullets-bar',
29808 cls : 'roo-navigation-bar'
29815 cls : 'roo-navigation-bottom-bar'
29825 initEvents: function()
29830 onRender : function(ct, position)
29832 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29834 if(this.bullets.length){
29835 Roo.each(this.bullets, function(b){
29844 addItem : function(cfg)
29846 var item = new Roo.bootstrap.NavProgressItem(cfg);
29848 item.parentId = this.id;
29849 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29852 var top = new Roo.bootstrap.Element({
29854 cls : 'roo-navigation-bar-text'
29857 var bottom = new Roo.bootstrap.Element({
29859 cls : 'roo-navigation-bar-text'
29862 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29863 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29865 var topText = new Roo.bootstrap.Element({
29867 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29870 var bottomText = new Roo.bootstrap.Element({
29872 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29875 topText.onRender(top.el, null);
29876 bottomText.onRender(bottom.el, null);
29879 item.bottomEl = bottom;
29882 this.barItems.push(item);
29887 getActive : function()
29889 var active = false;
29891 Roo.each(this.barItems, function(v){
29893 if (!v.isActive()) {
29905 setActiveItem : function(item)
29909 Roo.each(this.barItems, function(v){
29910 if (v.rid == item.rid) {
29914 if (v.isActive()) {
29915 v.setActive(false);
29920 item.setActive(true);
29922 this.fireEvent('changed', this, item, prev);
29925 getBarItem: function(rid)
29929 Roo.each(this.barItems, function(e) {
29930 if (e.rid != rid) {
29941 indexOfItem : function(item)
29945 Roo.each(this.barItems, function(v, i){
29947 if (v.rid != item.rid) {
29958 setActiveNext : function()
29960 var i = this.indexOfItem(this.getActive());
29962 if (i > this.barItems.length) {
29966 this.setActiveItem(this.barItems[i+1]);
29969 setActivePrev : function()
29971 var i = this.indexOfItem(this.getActive());
29977 this.setActiveItem(this.barItems[i-1]);
29980 format : function()
29982 if(!this.barItems.length){
29986 var width = 100 / this.barItems.length;
29988 Roo.each(this.barItems, function(i){
29989 i.el.setStyle('width', width + '%');
29990 i.topEl.el.setStyle('width', width + '%');
29991 i.bottomEl.el.setStyle('width', width + '%');
30000 * Nav Progress Item
30005 * @class Roo.bootstrap.NavProgressItem
30006 * @extends Roo.bootstrap.Component
30007 * Bootstrap NavProgressItem class
30008 * @cfg {String} rid the reference id
30009 * @cfg {Boolean} active (true|false) Is item active default false
30010 * @cfg {Boolean} disabled (true|false) Is item active default false
30011 * @cfg {String} html
30012 * @cfg {String} position (top|bottom) text position default bottom
30013 * @cfg {String} icon show icon instead of number
30016 * Create a new NavProgressItem
30017 * @param {Object} config The config object
30019 Roo.bootstrap.NavProgressItem = function(config){
30020 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30025 * The raw click event for the entire grid.
30026 * @param {Roo.bootstrap.NavProgressItem} this
30027 * @param {Roo.EventObject} e
30034 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30040 position : 'bottom',
30043 getAutoCreate : function()
30045 var iconCls = 'roo-navigation-bar-item-icon';
30047 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30051 cls: 'roo-navigation-bar-item',
30061 cfg.cls += ' active';
30064 cfg.cls += ' disabled';
30070 disable : function()
30072 this.setDisabled(true);
30075 enable : function()
30077 this.setDisabled(false);
30080 initEvents: function()
30082 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30084 this.iconEl.on('click', this.onClick, this);
30087 onClick : function(e)
30089 e.preventDefault();
30095 if(this.fireEvent('click', this, e) === false){
30099 this.parent().setActiveItem(this);
30102 isActive: function ()
30104 return this.active;
30107 setActive : function(state)
30109 if(this.active == state){
30113 this.active = state;
30116 this.el.addClass('active');
30120 this.el.removeClass('active');
30125 setDisabled : function(state)
30127 if(this.disabled == state){
30131 this.disabled = state;
30134 this.el.addClass('disabled');
30138 this.el.removeClass('disabled');
30141 tooltipEl : function()
30143 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30156 * @class Roo.bootstrap.FieldLabel
30157 * @extends Roo.bootstrap.Component
30158 * Bootstrap FieldLabel class
30159 * @cfg {String} html contents of the element
30160 * @cfg {String} tag tag of the element default label
30161 * @cfg {String} cls class of the element
30162 * @cfg {String} target label target
30163 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30164 * @cfg {String} invalidClass default "text-warning"
30165 * @cfg {String} validClass default "text-success"
30166 * @cfg {String} iconTooltip default "This field is required"
30167 * @cfg {String} indicatorpos (left|right) default left
30170 * Create a new FieldLabel
30171 * @param {Object} config The config object
30174 Roo.bootstrap.FieldLabel = function(config){
30175 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30180 * Fires after the field has been marked as invalid.
30181 * @param {Roo.form.FieldLabel} this
30182 * @param {String} msg The validation message
30187 * Fires after the field has been validated with no errors.
30188 * @param {Roo.form.FieldLabel} this
30194 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30201 invalidClass : 'has-warning',
30202 validClass : 'has-success',
30203 iconTooltip : 'This field is required',
30204 indicatorpos : 'left',
30206 getAutoCreate : function(){
30209 if (!this.allowBlank) {
30215 cls : 'roo-bootstrap-field-label ' + this.cls,
30220 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30221 tooltip : this.iconTooltip
30230 if(this.indicatorpos == 'right'){
30233 cls : 'roo-bootstrap-field-label ' + this.cls,
30242 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30243 tooltip : this.iconTooltip
30252 initEvents: function()
30254 Roo.bootstrap.Element.superclass.initEvents.call(this);
30256 this.indicator = this.indicatorEl();
30258 if(this.indicator){
30259 this.indicator.removeClass('visible');
30260 this.indicator.addClass('invisible');
30263 Roo.bootstrap.FieldLabel.register(this);
30266 indicatorEl : function()
30268 var indicator = this.el.select('i.roo-required-indicator',true).first();
30279 * Mark this field as valid
30281 markValid : function()
30283 if(this.indicator){
30284 this.indicator.removeClass('visible');
30285 this.indicator.addClass('invisible');
30288 this.el.removeClass(this.invalidClass);
30290 this.el.addClass(this.validClass);
30292 this.fireEvent('valid', this);
30296 * Mark this field as invalid
30297 * @param {String} msg The validation message
30299 markInvalid : function(msg)
30301 if(this.indicator){
30302 this.indicator.removeClass('invisible');
30303 this.indicator.addClass('visible');
30306 this.el.removeClass(this.validClass);
30308 this.el.addClass(this.invalidClass);
30310 this.fireEvent('invalid', this, msg);
30316 Roo.apply(Roo.bootstrap.FieldLabel, {
30321 * register a FieldLabel Group
30322 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30324 register : function(label)
30326 if(this.groups.hasOwnProperty(label.target)){
30330 this.groups[label.target] = label;
30334 * fetch a FieldLabel Group based on the target
30335 * @param {string} target
30336 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30338 get: function(target) {
30339 if (typeof(this.groups[target]) == 'undefined') {
30343 return this.groups[target] ;
30352 * page DateSplitField.
30358 * @class Roo.bootstrap.DateSplitField
30359 * @extends Roo.bootstrap.Component
30360 * Bootstrap DateSplitField class
30361 * @cfg {string} fieldLabel - the label associated
30362 * @cfg {Number} labelWidth set the width of label (0-12)
30363 * @cfg {String} labelAlign (top|left)
30364 * @cfg {Boolean} dayAllowBlank (true|false) default false
30365 * @cfg {Boolean} monthAllowBlank (true|false) default false
30366 * @cfg {Boolean} yearAllowBlank (true|false) default false
30367 * @cfg {string} dayPlaceholder
30368 * @cfg {string} monthPlaceholder
30369 * @cfg {string} yearPlaceholder
30370 * @cfg {string} dayFormat default 'd'
30371 * @cfg {string} monthFormat default 'm'
30372 * @cfg {string} yearFormat default 'Y'
30373 * @cfg {Number} labellg set the width of label (1-12)
30374 * @cfg {Number} labelmd set the width of label (1-12)
30375 * @cfg {Number} labelsm set the width of label (1-12)
30376 * @cfg {Number} labelxs set the width of label (1-12)
30380 * Create a new DateSplitField
30381 * @param {Object} config The config object
30384 Roo.bootstrap.DateSplitField = function(config){
30385 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30391 * getting the data of years
30392 * @param {Roo.bootstrap.DateSplitField} this
30393 * @param {Object} years
30398 * getting the data of days
30399 * @param {Roo.bootstrap.DateSplitField} this
30400 * @param {Object} days
30405 * Fires after the field has been marked as invalid.
30406 * @param {Roo.form.Field} this
30407 * @param {String} msg The validation message
30412 * Fires after the field has been validated with no errors.
30413 * @param {Roo.form.Field} this
30419 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30422 labelAlign : 'top',
30424 dayAllowBlank : false,
30425 monthAllowBlank : false,
30426 yearAllowBlank : false,
30427 dayPlaceholder : '',
30428 monthPlaceholder : '',
30429 yearPlaceholder : '',
30433 isFormField : true,
30439 getAutoCreate : function()
30443 cls : 'row roo-date-split-field-group',
30448 cls : 'form-hidden-field roo-date-split-field-group-value',
30454 var labelCls = 'col-md-12';
30455 var contentCls = 'col-md-4';
30457 if(this.fieldLabel){
30461 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30465 html : this.fieldLabel
30470 if(this.labelAlign == 'left'){
30472 if(this.labelWidth > 12){
30473 label.style = "width: " + this.labelWidth + 'px';
30476 if(this.labelWidth < 13 && this.labelmd == 0){
30477 this.labelmd = this.labelWidth;
30480 if(this.labellg > 0){
30481 labelCls = ' col-lg-' + this.labellg;
30482 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30485 if(this.labelmd > 0){
30486 labelCls = ' col-md-' + this.labelmd;
30487 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30490 if(this.labelsm > 0){
30491 labelCls = ' col-sm-' + this.labelsm;
30492 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30495 if(this.labelxs > 0){
30496 labelCls = ' col-xs-' + this.labelxs;
30497 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30501 label.cls += ' ' + labelCls;
30503 cfg.cn.push(label);
30506 Roo.each(['day', 'month', 'year'], function(t){
30509 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30516 inputEl: function ()
30518 return this.el.select('.roo-date-split-field-group-value', true).first();
30521 onRender : function(ct, position)
30525 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30527 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30529 this.dayField = new Roo.bootstrap.ComboBox({
30530 allowBlank : this.dayAllowBlank,
30531 alwaysQuery : true,
30532 displayField : 'value',
30535 forceSelection : true,
30537 placeholder : this.dayPlaceholder,
30538 selectOnFocus : true,
30539 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30540 triggerAction : 'all',
30542 valueField : 'value',
30543 store : new Roo.data.SimpleStore({
30544 data : (function() {
30546 _this.fireEvent('days', _this, days);
30549 fields : [ 'value' ]
30552 select : function (_self, record, index)
30554 _this.setValue(_this.getValue());
30559 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30561 this.monthField = new Roo.bootstrap.MonthField({
30562 after : '<i class=\"fa fa-calendar\"></i>',
30563 allowBlank : this.monthAllowBlank,
30564 placeholder : this.monthPlaceholder,
30567 render : function (_self)
30569 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30570 e.preventDefault();
30574 select : function (_self, oldvalue, newvalue)
30576 _this.setValue(_this.getValue());
30581 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30583 this.yearField = new Roo.bootstrap.ComboBox({
30584 allowBlank : this.yearAllowBlank,
30585 alwaysQuery : true,
30586 displayField : 'value',
30589 forceSelection : true,
30591 placeholder : this.yearPlaceholder,
30592 selectOnFocus : true,
30593 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30594 triggerAction : 'all',
30596 valueField : 'value',
30597 store : new Roo.data.SimpleStore({
30598 data : (function() {
30600 _this.fireEvent('years', _this, years);
30603 fields : [ 'value' ]
30606 select : function (_self, record, index)
30608 _this.setValue(_this.getValue());
30613 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30616 setValue : function(v, format)
30618 this.inputEl.dom.value = v;
30620 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30622 var d = Date.parseDate(v, f);
30629 this.setDay(d.format(this.dayFormat));
30630 this.setMonth(d.format(this.monthFormat));
30631 this.setYear(d.format(this.yearFormat));
30638 setDay : function(v)
30640 this.dayField.setValue(v);
30641 this.inputEl.dom.value = this.getValue();
30646 setMonth : function(v)
30648 this.monthField.setValue(v, true);
30649 this.inputEl.dom.value = this.getValue();
30654 setYear : function(v)
30656 this.yearField.setValue(v);
30657 this.inputEl.dom.value = this.getValue();
30662 getDay : function()
30664 return this.dayField.getValue();
30667 getMonth : function()
30669 return this.monthField.getValue();
30672 getYear : function()
30674 return this.yearField.getValue();
30677 getValue : function()
30679 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30681 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30691 this.inputEl.dom.value = '';
30696 validate : function()
30698 var d = this.dayField.validate();
30699 var m = this.monthField.validate();
30700 var y = this.yearField.validate();
30705 (!this.dayAllowBlank && !d) ||
30706 (!this.monthAllowBlank && !m) ||
30707 (!this.yearAllowBlank && !y)
30712 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30721 this.markInvalid();
30726 markValid : function()
30729 var label = this.el.select('label', true).first();
30730 var icon = this.el.select('i.fa-star', true).first();
30736 this.fireEvent('valid', this);
30740 * Mark this field as invalid
30741 * @param {String} msg The validation message
30743 markInvalid : function(msg)
30746 var label = this.el.select('label', true).first();
30747 var icon = this.el.select('i.fa-star', true).first();
30749 if(label && !icon){
30750 this.el.select('.roo-date-split-field-label', true).createChild({
30752 cls : 'text-danger fa fa-lg fa-star',
30753 tooltip : 'This field is required',
30754 style : 'margin-right:5px;'
30758 this.fireEvent('invalid', this, msg);
30761 clearInvalid : function()
30763 var label = this.el.select('label', true).first();
30764 var icon = this.el.select('i.fa-star', true).first();
30770 this.fireEvent('valid', this);
30773 getName: function()
30783 * http://masonry.desandro.com
30785 * The idea is to render all the bricks based on vertical width...
30787 * The original code extends 'outlayer' - we might need to use that....
30793 * @class Roo.bootstrap.LayoutMasonry
30794 * @extends Roo.bootstrap.Component
30795 * Bootstrap Layout Masonry class
30798 * Create a new Element
30799 * @param {Object} config The config object
30802 Roo.bootstrap.LayoutMasonry = function(config){
30804 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30808 Roo.bootstrap.LayoutMasonry.register(this);
30814 * Fire after layout the items
30815 * @param {Roo.bootstrap.LayoutMasonry} this
30816 * @param {Roo.EventObject} e
30823 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30826 * @cfg {Boolean} isLayoutInstant = no animation?
30828 isLayoutInstant : false, // needed?
30831 * @cfg {Number} boxWidth width of the columns
30836 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30841 * @cfg {Number} padWidth padding below box..
30846 * @cfg {Number} gutter gutter width..
30851 * @cfg {Number} maxCols maximum number of columns
30857 * @cfg {Boolean} isAutoInitial defalut true
30859 isAutoInitial : true,
30864 * @cfg {Boolean} isHorizontal defalut false
30866 isHorizontal : false,
30868 currentSize : null,
30874 bricks: null, //CompositeElement
30878 _isLayoutInited : false,
30880 // isAlternative : false, // only use for vertical layout...
30883 * @cfg {Number} alternativePadWidth padding below box..
30885 alternativePadWidth : 50,
30887 selectedBrick : [],
30889 getAutoCreate : function(){
30891 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30895 cls: 'blog-masonary-wrapper ' + this.cls,
30897 cls : 'mas-boxes masonary'
30904 getChildContainer: function( )
30906 if (this.boxesEl) {
30907 return this.boxesEl;
30910 this.boxesEl = this.el.select('.mas-boxes').first();
30912 return this.boxesEl;
30916 initEvents : function()
30920 if(this.isAutoInitial){
30921 Roo.log('hook children rendered');
30922 this.on('childrenrendered', function() {
30923 Roo.log('children rendered');
30929 initial : function()
30931 this.selectedBrick = [];
30933 this.currentSize = this.el.getBox(true);
30935 Roo.EventManager.onWindowResize(this.resize, this);
30937 if(!this.isAutoInitial){
30945 //this.layout.defer(500,this);
30949 resize : function()
30951 var cs = this.el.getBox(true);
30954 this.currentSize.width == cs.width &&
30955 this.currentSize.x == cs.x &&
30956 this.currentSize.height == cs.height &&
30957 this.currentSize.y == cs.y
30959 Roo.log("no change in with or X or Y");
30963 this.currentSize = cs;
30969 layout : function()
30971 this._resetLayout();
30973 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30975 this.layoutItems( isInstant );
30977 this._isLayoutInited = true;
30979 this.fireEvent('layout', this);
30983 _resetLayout : function()
30985 if(this.isHorizontal){
30986 this.horizontalMeasureColumns();
30990 this.verticalMeasureColumns();
30994 verticalMeasureColumns : function()
30996 this.getContainerWidth();
30998 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30999 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31003 var boxWidth = this.boxWidth + this.padWidth;
31005 if(this.containerWidth < this.boxWidth){
31006 boxWidth = this.containerWidth
31009 var containerWidth = this.containerWidth;
31011 var cols = Math.floor(containerWidth / boxWidth);
31013 this.cols = Math.max( cols, 1 );
31015 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31017 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31019 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31021 this.colWidth = boxWidth + avail - this.padWidth;
31023 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31024 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31027 horizontalMeasureColumns : function()
31029 this.getContainerWidth();
31031 var boxWidth = this.boxWidth;
31033 if(this.containerWidth < boxWidth){
31034 boxWidth = this.containerWidth;
31037 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31039 this.el.setHeight(boxWidth);
31043 getContainerWidth : function()
31045 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31048 layoutItems : function( isInstant )
31050 Roo.log(this.bricks);
31052 var items = Roo.apply([], this.bricks);
31054 if(this.isHorizontal){
31055 this._horizontalLayoutItems( items , isInstant );
31059 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31060 // this._verticalAlternativeLayoutItems( items , isInstant );
31064 this._verticalLayoutItems( items , isInstant );
31068 _verticalLayoutItems : function ( items , isInstant)
31070 if ( !items || !items.length ) {
31075 ['xs', 'xs', 'xs', 'tall'],
31076 ['xs', 'xs', 'tall'],
31077 ['xs', 'xs', 'sm'],
31078 ['xs', 'xs', 'xs'],
31084 ['sm', 'xs', 'xs'],
31088 ['tall', 'xs', 'xs', 'xs'],
31089 ['tall', 'xs', 'xs'],
31101 Roo.each(items, function(item, k){
31103 switch (item.size) {
31104 // these layouts take up a full box,
31115 boxes.push([item]);
31138 var filterPattern = function(box, length)
31146 var pattern = box.slice(0, length);
31150 Roo.each(pattern, function(i){
31151 format.push(i.size);
31154 Roo.each(standard, function(s){
31156 if(String(s) != String(format)){
31165 if(!match && length == 1){
31170 filterPattern(box, length - 1);
31174 queue.push(pattern);
31176 box = box.slice(length, box.length);
31178 filterPattern(box, 4);
31184 Roo.each(boxes, function(box, k){
31190 if(box.length == 1){
31195 filterPattern(box, 4);
31199 this._processVerticalLayoutQueue( queue, isInstant );
31203 // _verticalAlternativeLayoutItems : function( items , isInstant )
31205 // if ( !items || !items.length ) {
31209 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31213 _horizontalLayoutItems : function ( items , isInstant)
31215 if ( !items || !items.length || items.length < 3) {
31221 var eItems = items.slice(0, 3);
31223 items = items.slice(3, items.length);
31226 ['xs', 'xs', 'xs', 'wide'],
31227 ['xs', 'xs', 'wide'],
31228 ['xs', 'xs', 'sm'],
31229 ['xs', 'xs', 'xs'],
31235 ['sm', 'xs', 'xs'],
31239 ['wide', 'xs', 'xs', 'xs'],
31240 ['wide', 'xs', 'xs'],
31253 Roo.each(items, function(item, k){
31255 switch (item.size) {
31266 boxes.push([item]);
31290 var filterPattern = function(box, length)
31298 var pattern = box.slice(0, length);
31302 Roo.each(pattern, function(i){
31303 format.push(i.size);
31306 Roo.each(standard, function(s){
31308 if(String(s) != String(format)){
31317 if(!match && length == 1){
31322 filterPattern(box, length - 1);
31326 queue.push(pattern);
31328 box = box.slice(length, box.length);
31330 filterPattern(box, 4);
31336 Roo.each(boxes, function(box, k){
31342 if(box.length == 1){
31347 filterPattern(box, 4);
31354 var pos = this.el.getBox(true);
31358 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31360 var hit_end = false;
31362 Roo.each(queue, function(box){
31366 Roo.each(box, function(b){
31368 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31378 Roo.each(box, function(b){
31380 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31383 mx = Math.max(mx, b.x);
31387 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31391 Roo.each(box, function(b){
31393 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31407 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31410 /** Sets position of item in DOM
31411 * @param {Element} item
31412 * @param {Number} x - horizontal position
31413 * @param {Number} y - vertical position
31414 * @param {Boolean} isInstant - disables transitions
31416 _processVerticalLayoutQueue : function( queue, isInstant )
31418 var pos = this.el.getBox(true);
31423 for (var i = 0; i < this.cols; i++){
31427 Roo.each(queue, function(box, k){
31429 var col = k % this.cols;
31431 Roo.each(box, function(b,kk){
31433 b.el.position('absolute');
31435 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31436 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31438 if(b.size == 'md-left' || b.size == 'md-right'){
31439 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31440 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31443 b.el.setWidth(width);
31444 b.el.setHeight(height);
31446 b.el.select('iframe',true).setSize(width,height);
31450 for (var i = 0; i < this.cols; i++){
31452 if(maxY[i] < maxY[col]){
31457 col = Math.min(col, i);
31461 x = pos.x + col * (this.colWidth + this.padWidth);
31465 var positions = [];
31467 switch (box.length){
31469 positions = this.getVerticalOneBoxColPositions(x, y, box);
31472 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31475 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31478 positions = this.getVerticalFourBoxColPositions(x, y, box);
31484 Roo.each(box, function(b,kk){
31486 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31488 var sz = b.el.getSize();
31490 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31498 for (var i = 0; i < this.cols; i++){
31499 mY = Math.max(mY, maxY[i]);
31502 this.el.setHeight(mY - pos.y);
31506 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31508 // var pos = this.el.getBox(true);
31511 // var maxX = pos.right;
31513 // var maxHeight = 0;
31515 // Roo.each(items, function(item, k){
31519 // item.el.position('absolute');
31521 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31523 // item.el.setWidth(width);
31525 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31527 // item.el.setHeight(height);
31530 // item.el.setXY([x, y], isInstant ? false : true);
31532 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31535 // y = y + height + this.alternativePadWidth;
31537 // maxHeight = maxHeight + height + this.alternativePadWidth;
31541 // this.el.setHeight(maxHeight);
31545 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31547 var pos = this.el.getBox(true);
31552 var maxX = pos.right;
31554 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31556 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31558 Roo.each(queue, function(box, k){
31560 Roo.each(box, function(b, kk){
31562 b.el.position('absolute');
31564 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31565 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31567 if(b.size == 'md-left' || b.size == 'md-right'){
31568 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31569 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31572 b.el.setWidth(width);
31573 b.el.setHeight(height);
31581 var positions = [];
31583 switch (box.length){
31585 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31588 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31591 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31594 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31600 Roo.each(box, function(b,kk){
31602 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31604 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31612 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31614 Roo.each(eItems, function(b,k){
31616 b.size = (k == 0) ? 'sm' : 'xs';
31617 b.x = (k == 0) ? 2 : 1;
31618 b.y = (k == 0) ? 2 : 1;
31620 b.el.position('absolute');
31622 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31624 b.el.setWidth(width);
31626 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31628 b.el.setHeight(height);
31632 var positions = [];
31635 x : maxX - this.unitWidth * 2 - this.gutter,
31640 x : maxX - this.unitWidth,
31641 y : minY + (this.unitWidth + this.gutter) * 2
31645 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31649 Roo.each(eItems, function(b,k){
31651 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31657 getVerticalOneBoxColPositions : function(x, y, box)
31661 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31663 if(box[0].size == 'md-left'){
31667 if(box[0].size == 'md-right'){
31672 x : x + (this.unitWidth + this.gutter) * rand,
31679 getVerticalTwoBoxColPositions : function(x, y, box)
31683 if(box[0].size == 'xs'){
31687 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31691 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31705 x : x + (this.unitWidth + this.gutter) * 2,
31706 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31713 getVerticalThreeBoxColPositions : function(x, y, box)
31717 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31725 x : x + (this.unitWidth + this.gutter) * 1,
31730 x : x + (this.unitWidth + this.gutter) * 2,
31738 if(box[0].size == 'xs' && box[1].size == 'xs'){
31747 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31751 x : x + (this.unitWidth + this.gutter) * 1,
31765 x : x + (this.unitWidth + this.gutter) * 2,
31770 x : x + (this.unitWidth + this.gutter) * 2,
31771 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31778 getVerticalFourBoxColPositions : function(x, y, box)
31782 if(box[0].size == 'xs'){
31791 y : y + (this.unitHeight + this.gutter) * 1
31796 y : y + (this.unitHeight + this.gutter) * 2
31800 x : x + (this.unitWidth + this.gutter) * 1,
31814 x : x + (this.unitWidth + this.gutter) * 2,
31819 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31820 y : y + (this.unitHeight + this.gutter) * 1
31824 x : x + (this.unitWidth + this.gutter) * 2,
31825 y : y + (this.unitWidth + this.gutter) * 2
31832 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31836 if(box[0].size == 'md-left'){
31838 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31845 if(box[0].size == 'md-right'){
31847 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31848 y : minY + (this.unitWidth + this.gutter) * 1
31854 var rand = Math.floor(Math.random() * (4 - box[0].y));
31857 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31858 y : minY + (this.unitWidth + this.gutter) * rand
31865 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31869 if(box[0].size == 'xs'){
31872 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31877 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31878 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31886 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31891 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31892 y : minY + (this.unitWidth + this.gutter) * 2
31899 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31903 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31906 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31911 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31912 y : minY + (this.unitWidth + this.gutter) * 1
31916 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31917 y : minY + (this.unitWidth + this.gutter) * 2
31924 if(box[0].size == 'xs' && box[1].size == 'xs'){
31927 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31932 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31937 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31938 y : minY + (this.unitWidth + this.gutter) * 1
31946 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31951 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31952 y : minY + (this.unitWidth + this.gutter) * 2
31956 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31957 y : minY + (this.unitWidth + this.gutter) * 2
31964 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31968 if(box[0].size == 'xs'){
31971 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31976 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31981 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),
31986 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31987 y : minY + (this.unitWidth + this.gutter) * 1
31995 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32000 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32001 y : minY + (this.unitWidth + this.gutter) * 2
32005 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32006 y : minY + (this.unitWidth + this.gutter) * 2
32010 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),
32011 y : minY + (this.unitWidth + this.gutter) * 2
32019 * remove a Masonry Brick
32020 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32022 removeBrick : function(brick_id)
32028 for (var i = 0; i<this.bricks.length; i++) {
32029 if (this.bricks[i].id == brick_id) {
32030 this.bricks.splice(i,1);
32031 this.el.dom.removeChild(Roo.get(brick_id).dom);
32038 * adds a Masonry Brick
32039 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32041 addBrick : function(cfg)
32043 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32044 //this.register(cn);
32045 cn.parentId = this.id;
32046 cn.render(this.el);
32051 * register a Masonry Brick
32052 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32055 register : function(brick)
32057 this.bricks.push(brick);
32058 brick.masonryId = this.id;
32062 * clear all the Masonry Brick
32064 clearAll : function()
32067 //this.getChildContainer().dom.innerHTML = "";
32068 this.el.dom.innerHTML = '';
32071 getSelected : function()
32073 if (!this.selectedBrick) {
32077 return this.selectedBrick;
32081 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32085 * register a Masonry Layout
32086 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32089 register : function(layout)
32091 this.groups[layout.id] = layout;
32094 * fetch a Masonry Layout based on the masonry layout ID
32095 * @param {string} the masonry layout to add
32096 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32099 get: function(layout_id) {
32100 if (typeof(this.groups[layout_id]) == 'undefined') {
32103 return this.groups[layout_id] ;
32115 * http://masonry.desandro.com
32117 * The idea is to render all the bricks based on vertical width...
32119 * The original code extends 'outlayer' - we might need to use that....
32125 * @class Roo.bootstrap.LayoutMasonryAuto
32126 * @extends Roo.bootstrap.Component
32127 * Bootstrap Layout Masonry class
32130 * Create a new Element
32131 * @param {Object} config The config object
32134 Roo.bootstrap.LayoutMasonryAuto = function(config){
32135 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32138 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32141 * @cfg {Boolean} isFitWidth - resize the width..
32143 isFitWidth : false, // options..
32145 * @cfg {Boolean} isOriginLeft = left align?
32147 isOriginLeft : true,
32149 * @cfg {Boolean} isOriginTop = top align?
32151 isOriginTop : false,
32153 * @cfg {Boolean} isLayoutInstant = no animation?
32155 isLayoutInstant : false, // needed?
32157 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32159 isResizingContainer : true,
32161 * @cfg {Number} columnWidth width of the columns
32167 * @cfg {Number} maxCols maximum number of columns
32172 * @cfg {Number} padHeight padding below box..
32178 * @cfg {Boolean} isAutoInitial defalut true
32181 isAutoInitial : true,
32187 initialColumnWidth : 0,
32188 currentSize : null,
32190 colYs : null, // array.
32197 bricks: null, //CompositeElement
32198 cols : 0, // array?
32199 // element : null, // wrapped now this.el
32200 _isLayoutInited : null,
32203 getAutoCreate : function(){
32207 cls: 'blog-masonary-wrapper ' + this.cls,
32209 cls : 'mas-boxes masonary'
32216 getChildContainer: function( )
32218 if (this.boxesEl) {
32219 return this.boxesEl;
32222 this.boxesEl = this.el.select('.mas-boxes').first();
32224 return this.boxesEl;
32228 initEvents : function()
32232 if(this.isAutoInitial){
32233 Roo.log('hook children rendered');
32234 this.on('childrenrendered', function() {
32235 Roo.log('children rendered');
32242 initial : function()
32244 this.reloadItems();
32246 this.currentSize = this.el.getBox(true);
32248 /// was window resize... - let's see if this works..
32249 Roo.EventManager.onWindowResize(this.resize, this);
32251 if(!this.isAutoInitial){
32256 this.layout.defer(500,this);
32259 reloadItems: function()
32261 this.bricks = this.el.select('.masonry-brick', true);
32263 this.bricks.each(function(b) {
32264 //Roo.log(b.getSize());
32265 if (!b.attr('originalwidth')) {
32266 b.attr('originalwidth', b.getSize().width);
32271 Roo.log(this.bricks.elements.length);
32274 resize : function()
32277 var cs = this.el.getBox(true);
32279 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32280 Roo.log("no change in with or X");
32283 this.currentSize = cs;
32287 layout : function()
32290 this._resetLayout();
32291 //this._manageStamps();
32293 // don't animate first layout
32294 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32295 this.layoutItems( isInstant );
32297 // flag for initalized
32298 this._isLayoutInited = true;
32301 layoutItems : function( isInstant )
32303 //var items = this._getItemsForLayout( this.items );
32304 // original code supports filtering layout items.. we just ignore it..
32306 this._layoutItems( this.bricks , isInstant );
32308 this._postLayout();
32310 _layoutItems : function ( items , isInstant)
32312 //this.fireEvent( 'layout', this, items );
32315 if ( !items || !items.elements.length ) {
32316 // no items, emit event with empty array
32321 items.each(function(item) {
32322 Roo.log("layout item");
32324 // get x/y object from method
32325 var position = this._getItemLayoutPosition( item );
32327 position.item = item;
32328 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32329 queue.push( position );
32332 this._processLayoutQueue( queue );
32334 /** Sets position of item in DOM
32335 * @param {Element} item
32336 * @param {Number} x - horizontal position
32337 * @param {Number} y - vertical position
32338 * @param {Boolean} isInstant - disables transitions
32340 _processLayoutQueue : function( queue )
32342 for ( var i=0, len = queue.length; i < len; i++ ) {
32343 var obj = queue[i];
32344 obj.item.position('absolute');
32345 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32351 * Any logic you want to do after each layout,
32352 * i.e. size the container
32354 _postLayout : function()
32356 this.resizeContainer();
32359 resizeContainer : function()
32361 if ( !this.isResizingContainer ) {
32364 var size = this._getContainerSize();
32366 this.el.setSize(size.width,size.height);
32367 this.boxesEl.setSize(size.width,size.height);
32373 _resetLayout : function()
32375 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32376 this.colWidth = this.el.getWidth();
32377 //this.gutter = this.el.getWidth();
32379 this.measureColumns();
32385 this.colYs.push( 0 );
32391 measureColumns : function()
32393 this.getContainerWidth();
32394 // if columnWidth is 0, default to outerWidth of first item
32395 if ( !this.columnWidth ) {
32396 var firstItem = this.bricks.first();
32397 Roo.log(firstItem);
32398 this.columnWidth = this.containerWidth;
32399 if (firstItem && firstItem.attr('originalwidth') ) {
32400 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32402 // columnWidth fall back to item of first element
32403 Roo.log("set column width?");
32404 this.initialColumnWidth = this.columnWidth ;
32406 // if first elem has no width, default to size of container
32411 if (this.initialColumnWidth) {
32412 this.columnWidth = this.initialColumnWidth;
32417 // column width is fixed at the top - however if container width get's smaller we should
32420 // this bit calcs how man columns..
32422 var columnWidth = this.columnWidth += this.gutter;
32424 // calculate columns
32425 var containerWidth = this.containerWidth + this.gutter;
32427 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32428 // fix rounding errors, typically with gutters
32429 var excess = columnWidth - containerWidth % columnWidth;
32432 // if overshoot is less than a pixel, round up, otherwise floor it
32433 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32434 cols = Math[ mathMethod ]( cols );
32435 this.cols = Math.max( cols, 1 );
32436 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32438 // padding positioning..
32439 var totalColWidth = this.cols * this.columnWidth;
32440 var padavail = this.containerWidth - totalColWidth;
32441 // so for 2 columns - we need 3 'pads'
32443 var padNeeded = (1+this.cols) * this.padWidth;
32445 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32447 this.columnWidth += padExtra
32448 //this.padWidth = Math.floor(padavail / ( this.cols));
32450 // adjust colum width so that padding is fixed??
32452 // we have 3 columns ... total = width * 3
32453 // we have X left over... that should be used by
32455 //if (this.expandC) {
32463 getContainerWidth : function()
32465 /* // container is parent if fit width
32466 var container = this.isFitWidth ? this.element.parentNode : this.element;
32467 // check that this.size and size are there
32468 // IE8 triggers resize on body size change, so they might not be
32470 var size = getSize( container ); //FIXME
32471 this.containerWidth = size && size.innerWidth; //FIXME
32474 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32478 _getItemLayoutPosition : function( item ) // what is item?
32480 // we resize the item to our columnWidth..
32482 item.setWidth(this.columnWidth);
32483 item.autoBoxAdjust = false;
32485 var sz = item.getSize();
32487 // how many columns does this brick span
32488 var remainder = this.containerWidth % this.columnWidth;
32490 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32491 // round if off by 1 pixel, otherwise use ceil
32492 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32493 colSpan = Math.min( colSpan, this.cols );
32495 // normally this should be '1' as we dont' currently allow multi width columns..
32497 var colGroup = this._getColGroup( colSpan );
32498 // get the minimum Y value from the columns
32499 var minimumY = Math.min.apply( Math, colGroup );
32500 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32502 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32504 // position the brick
32506 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32507 y: this.currentSize.y + minimumY + this.padHeight
32511 // apply setHeight to necessary columns
32512 var setHeight = minimumY + sz.height + this.padHeight;
32513 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32515 var setSpan = this.cols + 1 - colGroup.length;
32516 for ( var i = 0; i < setSpan; i++ ) {
32517 this.colYs[ shortColIndex + i ] = setHeight ;
32524 * @param {Number} colSpan - number of columns the element spans
32525 * @returns {Array} colGroup
32527 _getColGroup : function( colSpan )
32529 if ( colSpan < 2 ) {
32530 // if brick spans only one column, use all the column Ys
32535 // how many different places could this brick fit horizontally
32536 var groupCount = this.cols + 1 - colSpan;
32537 // for each group potential horizontal position
32538 for ( var i = 0; i < groupCount; i++ ) {
32539 // make an array of colY values for that one group
32540 var groupColYs = this.colYs.slice( i, i + colSpan );
32541 // and get the max value of the array
32542 colGroup[i] = Math.max.apply( Math, groupColYs );
32547 _manageStamp : function( stamp )
32549 var stampSize = stamp.getSize();
32550 var offset = stamp.getBox();
32551 // get the columns that this stamp affects
32552 var firstX = this.isOriginLeft ? offset.x : offset.right;
32553 var lastX = firstX + stampSize.width;
32554 var firstCol = Math.floor( firstX / this.columnWidth );
32555 firstCol = Math.max( 0, firstCol );
32557 var lastCol = Math.floor( lastX / this.columnWidth );
32558 // lastCol should not go over if multiple of columnWidth #425
32559 lastCol -= lastX % this.columnWidth ? 0 : 1;
32560 lastCol = Math.min( this.cols - 1, lastCol );
32562 // set colYs to bottom of the stamp
32563 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32566 for ( var i = firstCol; i <= lastCol; i++ ) {
32567 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32572 _getContainerSize : function()
32574 this.maxY = Math.max.apply( Math, this.colYs );
32579 if ( this.isFitWidth ) {
32580 size.width = this._getContainerFitWidth();
32586 _getContainerFitWidth : function()
32588 var unusedCols = 0;
32589 // count unused columns
32592 if ( this.colYs[i] !== 0 ) {
32597 // fit container to columns that have been used
32598 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32601 needsResizeLayout : function()
32603 var previousWidth = this.containerWidth;
32604 this.getContainerWidth();
32605 return previousWidth !== this.containerWidth;
32620 * @class Roo.bootstrap.MasonryBrick
32621 * @extends Roo.bootstrap.Component
32622 * Bootstrap MasonryBrick class
32625 * Create a new MasonryBrick
32626 * @param {Object} config The config object
32629 Roo.bootstrap.MasonryBrick = function(config){
32631 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32633 Roo.bootstrap.MasonryBrick.register(this);
32639 * When a MasonryBrick is clcik
32640 * @param {Roo.bootstrap.MasonryBrick} this
32641 * @param {Roo.EventObject} e
32647 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32650 * @cfg {String} title
32654 * @cfg {String} html
32658 * @cfg {String} bgimage
32662 * @cfg {String} videourl
32666 * @cfg {String} cls
32670 * @cfg {String} href
32674 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32679 * @cfg {String} placetitle (center|bottom)
32684 * @cfg {Boolean} isFitContainer defalut true
32686 isFitContainer : true,
32689 * @cfg {Boolean} preventDefault defalut false
32691 preventDefault : false,
32694 * @cfg {Boolean} inverse defalut false
32696 maskInverse : false,
32698 getAutoCreate : function()
32700 if(!this.isFitContainer){
32701 return this.getSplitAutoCreate();
32704 var cls = 'masonry-brick masonry-brick-full';
32706 if(this.href.length){
32707 cls += ' masonry-brick-link';
32710 if(this.bgimage.length){
32711 cls += ' masonry-brick-image';
32714 if(this.maskInverse){
32715 cls += ' mask-inverse';
32718 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32719 cls += ' enable-mask';
32723 cls += ' masonry-' + this.size + '-brick';
32726 if(this.placetitle.length){
32728 switch (this.placetitle) {
32730 cls += ' masonry-center-title';
32733 cls += ' masonry-bottom-title';
32740 if(!this.html.length && !this.bgimage.length){
32741 cls += ' masonry-center-title';
32744 if(!this.html.length && this.bgimage.length){
32745 cls += ' masonry-bottom-title';
32750 cls += ' ' + this.cls;
32754 tag: (this.href.length) ? 'a' : 'div',
32759 cls: 'masonry-brick-mask'
32763 cls: 'masonry-brick-paragraph',
32769 if(this.href.length){
32770 cfg.href = this.href;
32773 var cn = cfg.cn[1].cn;
32775 if(this.title.length){
32778 cls: 'masonry-brick-title',
32783 if(this.html.length){
32786 cls: 'masonry-brick-text',
32791 if (!this.title.length && !this.html.length) {
32792 cfg.cn[1].cls += ' hide';
32795 if(this.bgimage.length){
32798 cls: 'masonry-brick-image-view',
32803 if(this.videourl.length){
32804 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32805 // youtube support only?
32808 cls: 'masonry-brick-image-view',
32811 allowfullscreen : true
32819 getSplitAutoCreate : function()
32821 var cls = 'masonry-brick masonry-brick-split';
32823 if(this.href.length){
32824 cls += ' masonry-brick-link';
32827 if(this.bgimage.length){
32828 cls += ' masonry-brick-image';
32832 cls += ' masonry-' + this.size + '-brick';
32835 switch (this.placetitle) {
32837 cls += ' masonry-center-title';
32840 cls += ' masonry-bottom-title';
32843 if(!this.bgimage.length){
32844 cls += ' masonry-center-title';
32847 if(this.bgimage.length){
32848 cls += ' masonry-bottom-title';
32854 cls += ' ' + this.cls;
32858 tag: (this.href.length) ? 'a' : 'div',
32863 cls: 'masonry-brick-split-head',
32867 cls: 'masonry-brick-paragraph',
32874 cls: 'masonry-brick-split-body',
32880 if(this.href.length){
32881 cfg.href = this.href;
32884 if(this.title.length){
32885 cfg.cn[0].cn[0].cn.push({
32887 cls: 'masonry-brick-title',
32892 if(this.html.length){
32893 cfg.cn[1].cn.push({
32895 cls: 'masonry-brick-text',
32900 if(this.bgimage.length){
32901 cfg.cn[0].cn.push({
32903 cls: 'masonry-brick-image-view',
32908 if(this.videourl.length){
32909 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32910 // youtube support only?
32911 cfg.cn[0].cn.cn.push({
32913 cls: 'masonry-brick-image-view',
32916 allowfullscreen : true
32923 initEvents: function()
32925 switch (this.size) {
32958 this.el.on('touchstart', this.onTouchStart, this);
32959 this.el.on('touchmove', this.onTouchMove, this);
32960 this.el.on('touchend', this.onTouchEnd, this);
32961 this.el.on('contextmenu', this.onContextMenu, this);
32963 this.el.on('mouseenter' ,this.enter, this);
32964 this.el.on('mouseleave', this.leave, this);
32965 this.el.on('click', this.onClick, this);
32968 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32969 this.parent().bricks.push(this);
32974 onClick: function(e, el)
32976 var time = this.endTimer - this.startTimer;
32977 // Roo.log(e.preventDefault());
32980 e.preventDefault();
32985 if(!this.preventDefault){
32989 e.preventDefault();
32991 if (this.activeClass != '') {
32992 this.selectBrick();
32995 this.fireEvent('click', this, e);
32998 enter: function(e, el)
33000 e.preventDefault();
33002 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33006 if(this.bgimage.length && this.html.length){
33007 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33011 leave: function(e, el)
33013 e.preventDefault();
33015 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33019 if(this.bgimage.length && this.html.length){
33020 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33024 onTouchStart: function(e, el)
33026 // e.preventDefault();
33028 this.touchmoved = false;
33030 if(!this.isFitContainer){
33034 if(!this.bgimage.length || !this.html.length){
33038 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33040 this.timer = new Date().getTime();
33044 onTouchMove: function(e, el)
33046 this.touchmoved = true;
33049 onContextMenu : function(e,el)
33051 e.preventDefault();
33052 e.stopPropagation();
33056 onTouchEnd: function(e, el)
33058 // e.preventDefault();
33060 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33067 if(!this.bgimage.length || !this.html.length){
33069 if(this.href.length){
33070 window.location.href = this.href;
33076 if(!this.isFitContainer){
33080 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33082 window.location.href = this.href;
33085 //selection on single brick only
33086 selectBrick : function() {
33088 if (!this.parentId) {
33092 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33093 var index = m.selectedBrick.indexOf(this.id);
33096 m.selectedBrick.splice(index,1);
33097 this.el.removeClass(this.activeClass);
33101 for(var i = 0; i < m.selectedBrick.length; i++) {
33102 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33103 b.el.removeClass(b.activeClass);
33106 m.selectedBrick = [];
33108 m.selectedBrick.push(this.id);
33109 this.el.addClass(this.activeClass);
33113 isSelected : function(){
33114 return this.el.hasClass(this.activeClass);
33119 Roo.apply(Roo.bootstrap.MasonryBrick, {
33122 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33124 * register a Masonry Brick
33125 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33128 register : function(brick)
33130 //this.groups[brick.id] = brick;
33131 this.groups.add(brick.id, brick);
33134 * fetch a masonry brick based on the masonry brick ID
33135 * @param {string} the masonry brick to add
33136 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33139 get: function(brick_id)
33141 // if (typeof(this.groups[brick_id]) == 'undefined') {
33144 // return this.groups[brick_id] ;
33146 if(this.groups.key(brick_id)) {
33147 return this.groups.key(brick_id);
33165 * @class Roo.bootstrap.Brick
33166 * @extends Roo.bootstrap.Component
33167 * Bootstrap Brick class
33170 * Create a new Brick
33171 * @param {Object} config The config object
33174 Roo.bootstrap.Brick = function(config){
33175 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33181 * When a Brick is click
33182 * @param {Roo.bootstrap.Brick} this
33183 * @param {Roo.EventObject} e
33189 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33192 * @cfg {String} title
33196 * @cfg {String} html
33200 * @cfg {String} bgimage
33204 * @cfg {String} cls
33208 * @cfg {String} href
33212 * @cfg {String} video
33216 * @cfg {Boolean} square
33220 getAutoCreate : function()
33222 var cls = 'roo-brick';
33224 if(this.href.length){
33225 cls += ' roo-brick-link';
33228 if(this.bgimage.length){
33229 cls += ' roo-brick-image';
33232 if(!this.html.length && !this.bgimage.length){
33233 cls += ' roo-brick-center-title';
33236 if(!this.html.length && this.bgimage.length){
33237 cls += ' roo-brick-bottom-title';
33241 cls += ' ' + this.cls;
33245 tag: (this.href.length) ? 'a' : 'div',
33250 cls: 'roo-brick-paragraph',
33256 if(this.href.length){
33257 cfg.href = this.href;
33260 var cn = cfg.cn[0].cn;
33262 if(this.title.length){
33265 cls: 'roo-brick-title',
33270 if(this.html.length){
33273 cls: 'roo-brick-text',
33280 if(this.bgimage.length){
33283 cls: 'roo-brick-image-view',
33291 initEvents: function()
33293 if(this.title.length || this.html.length){
33294 this.el.on('mouseenter' ,this.enter, this);
33295 this.el.on('mouseleave', this.leave, this);
33298 Roo.EventManager.onWindowResize(this.resize, this);
33300 if(this.bgimage.length){
33301 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33302 this.imageEl.on('load', this.onImageLoad, this);
33309 onImageLoad : function()
33314 resize : function()
33316 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33318 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33320 if(this.bgimage.length){
33321 var image = this.el.select('.roo-brick-image-view', true).first();
33323 image.setWidth(paragraph.getWidth());
33326 image.setHeight(paragraph.getWidth());
33329 this.el.setHeight(image.getHeight());
33330 paragraph.setHeight(image.getHeight());
33336 enter: function(e, el)
33338 e.preventDefault();
33340 if(this.bgimage.length){
33341 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33342 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33346 leave: function(e, el)
33348 e.preventDefault();
33350 if(this.bgimage.length){
33351 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33352 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33367 * @class Roo.bootstrap.NumberField
33368 * @extends Roo.bootstrap.Input
33369 * Bootstrap NumberField class
33375 * Create a new NumberField
33376 * @param {Object} config The config object
33379 Roo.bootstrap.NumberField = function(config){
33380 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33383 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33386 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33388 allowDecimals : true,
33390 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33392 decimalSeparator : ".",
33394 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33396 decimalPrecision : 2,
33398 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33400 allowNegative : true,
33403 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33407 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33409 minValue : Number.NEGATIVE_INFINITY,
33411 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33413 maxValue : Number.MAX_VALUE,
33415 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33417 minText : "The minimum value for this field is {0}",
33419 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33421 maxText : "The maximum value for this field is {0}",
33423 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33424 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33426 nanText : "{0} is not a valid number",
33428 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33430 thousandsDelimiter : false,
33432 * @cfg {String} valueAlign alignment of value
33434 valueAlign : "left",
33436 getAutoCreate : function()
33438 var hiddenInput = {
33442 cls: 'hidden-number-input'
33446 hiddenInput.name = this.name;
33451 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33453 this.name = hiddenInput.name;
33455 if(cfg.cn.length > 0) {
33456 cfg.cn.push(hiddenInput);
33463 initEvents : function()
33465 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33467 var allowed = "0123456789";
33469 if(this.allowDecimals){
33470 allowed += this.decimalSeparator;
33473 if(this.allowNegative){
33477 if(this.thousandsDelimiter) {
33481 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33483 var keyPress = function(e){
33485 var k = e.getKey();
33487 var c = e.getCharCode();
33490 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33491 allowed.indexOf(String.fromCharCode(c)) === -1
33497 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33501 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33506 this.el.on("keypress", keyPress, this);
33509 validateValue : function(value)
33512 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33516 var num = this.parseValue(value);
33519 this.markInvalid(String.format(this.nanText, value));
33523 if(num < this.minValue){
33524 this.markInvalid(String.format(this.minText, this.minValue));
33528 if(num > this.maxValue){
33529 this.markInvalid(String.format(this.maxText, this.maxValue));
33536 getValue : function()
33538 var v = this.hiddenEl().getValue();
33540 return this.fixPrecision(this.parseValue(v));
33543 parseValue : function(value)
33545 if(this.thousandsDelimiter) {
33547 r = new RegExp(",", "g");
33548 value = value.replace(r, "");
33551 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33552 return isNaN(value) ? '' : value;
33555 fixPrecision : function(value)
33557 if(this.thousandsDelimiter) {
33559 r = new RegExp(",", "g");
33560 value = value.replace(r, "");
33563 var nan = isNaN(value);
33565 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33566 return nan ? '' : value;
33568 return parseFloat(value).toFixed(this.decimalPrecision);
33571 setValue : function(v)
33573 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33579 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33581 this.inputEl().dom.value = (v == '') ? '' :
33582 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33584 if(!this.allowZero && v === '0') {
33585 this.hiddenEl().dom.value = '';
33586 this.inputEl().dom.value = '';
33593 decimalPrecisionFcn : function(v)
33595 return Math.floor(v);
33598 beforeBlur : function()
33600 var v = this.parseValue(this.getRawValue());
33602 if(v || v === 0 || v === ''){
33607 hiddenEl : function()
33609 return this.el.select('input.hidden-number-input',true).first();
33621 * @class Roo.bootstrap.DocumentSlider
33622 * @extends Roo.bootstrap.Component
33623 * Bootstrap DocumentSlider class
33626 * Create a new DocumentViewer
33627 * @param {Object} config The config object
33630 Roo.bootstrap.DocumentSlider = function(config){
33631 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33638 * Fire after initEvent
33639 * @param {Roo.bootstrap.DocumentSlider} this
33644 * Fire after update
33645 * @param {Roo.bootstrap.DocumentSlider} this
33651 * @param {Roo.bootstrap.DocumentSlider} this
33657 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33663 getAutoCreate : function()
33667 cls : 'roo-document-slider',
33671 cls : 'roo-document-slider-header',
33675 cls : 'roo-document-slider-header-title'
33681 cls : 'roo-document-slider-body',
33685 cls : 'roo-document-slider-prev',
33689 cls : 'fa fa-chevron-left'
33695 cls : 'roo-document-slider-thumb',
33699 cls : 'roo-document-slider-image'
33705 cls : 'roo-document-slider-next',
33709 cls : 'fa fa-chevron-right'
33721 initEvents : function()
33723 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33724 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33726 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33727 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33729 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33730 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33732 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33733 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33735 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33736 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33738 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33739 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33741 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33742 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33744 this.thumbEl.on('click', this.onClick, this);
33746 this.prevIndicator.on('click', this.prev, this);
33748 this.nextIndicator.on('click', this.next, this);
33752 initial : function()
33754 if(this.files.length){
33755 this.indicator = 1;
33759 this.fireEvent('initial', this);
33762 update : function()
33764 this.imageEl.attr('src', this.files[this.indicator - 1]);
33766 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33768 this.prevIndicator.show();
33770 if(this.indicator == 1){
33771 this.prevIndicator.hide();
33774 this.nextIndicator.show();
33776 if(this.indicator == this.files.length){
33777 this.nextIndicator.hide();
33780 this.thumbEl.scrollTo('top');
33782 this.fireEvent('update', this);
33785 onClick : function(e)
33787 e.preventDefault();
33789 this.fireEvent('click', this);
33794 e.preventDefault();
33796 this.indicator = Math.max(1, this.indicator - 1);
33803 e.preventDefault();
33805 this.indicator = Math.min(this.files.length, this.indicator + 1);
33819 * @class Roo.bootstrap.RadioSet
33820 * @extends Roo.bootstrap.Input
33821 * Bootstrap RadioSet class
33822 * @cfg {String} indicatorpos (left|right) default left
33823 * @cfg {Boolean} inline (true|false) inline the element (default true)
33824 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33826 * Create a new RadioSet
33827 * @param {Object} config The config object
33830 Roo.bootstrap.RadioSet = function(config){
33832 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33836 Roo.bootstrap.RadioSet.register(this);
33841 * Fires when the element is checked or unchecked.
33842 * @param {Roo.bootstrap.RadioSet} this This radio
33843 * @param {Roo.bootstrap.Radio} item The checked item
33848 * Fires when the element is click.
33849 * @param {Roo.bootstrap.RadioSet} this This radio set
33850 * @param {Roo.bootstrap.Radio} item The checked item
33851 * @param {Roo.EventObject} e The event object
33858 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33866 indicatorpos : 'left',
33868 getAutoCreate : function()
33872 cls : 'roo-radio-set-label',
33876 html : this.fieldLabel
33881 if(this.indicatorpos == 'left'){
33884 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33885 tooltip : 'This field is required'
33890 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33891 tooltip : 'This field is required'
33897 cls : 'roo-radio-set-items'
33900 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33902 if (align === 'left' && this.fieldLabel.length) {
33905 cls : "roo-radio-set-right",
33911 if(this.labelWidth > 12){
33912 label.style = "width: " + this.labelWidth + 'px';
33915 if(this.labelWidth < 13 && this.labelmd == 0){
33916 this.labelmd = this.labelWidth;
33919 if(this.labellg > 0){
33920 label.cls += ' col-lg-' + this.labellg;
33921 items.cls += ' col-lg-' + (12 - this.labellg);
33924 if(this.labelmd > 0){
33925 label.cls += ' col-md-' + this.labelmd;
33926 items.cls += ' col-md-' + (12 - this.labelmd);
33929 if(this.labelsm > 0){
33930 label.cls += ' col-sm-' + this.labelsm;
33931 items.cls += ' col-sm-' + (12 - this.labelsm);
33934 if(this.labelxs > 0){
33935 label.cls += ' col-xs-' + this.labelxs;
33936 items.cls += ' col-xs-' + (12 - this.labelxs);
33942 cls : 'roo-radio-set',
33946 cls : 'roo-radio-set-input',
33949 value : this.value ? this.value : ''
33956 if(this.weight.length){
33957 cfg.cls += ' roo-radio-' + this.weight;
33961 cfg.cls += ' roo-radio-set-inline';
33965 ['xs','sm','md','lg'].map(function(size){
33966 if (settings[size]) {
33967 cfg.cls += ' col-' + size + '-' + settings[size];
33975 initEvents : function()
33977 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33978 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33980 if(!this.fieldLabel.length){
33981 this.labelEl.hide();
33984 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33985 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33987 this.indicator = this.indicatorEl();
33989 if(this.indicator){
33990 this.indicator.addClass('invisible');
33993 this.originalValue = this.getValue();
33997 inputEl: function ()
33999 return this.el.select('.roo-radio-set-input', true).first();
34002 getChildContainer : function()
34004 return this.itemsEl;
34007 register : function(item)
34009 this.radioes.push(item);
34013 validate : function()
34015 if(this.getVisibilityEl().hasClass('hidden')){
34021 Roo.each(this.radioes, function(i){
34030 if(this.allowBlank) {
34034 if(this.disabled || valid){
34039 this.markInvalid();
34044 markValid : function()
34046 if(this.labelEl.isVisible(true)){
34047 this.indicatorEl().removeClass('visible');
34048 this.indicatorEl().addClass('invisible');
34051 this.el.removeClass([this.invalidClass, this.validClass]);
34052 this.el.addClass(this.validClass);
34054 this.fireEvent('valid', this);
34057 markInvalid : function(msg)
34059 if(this.allowBlank || this.disabled){
34063 if(this.labelEl.isVisible(true)){
34064 this.indicatorEl().removeClass('invisible');
34065 this.indicatorEl().addClass('visible');
34068 this.el.removeClass([this.invalidClass, this.validClass]);
34069 this.el.addClass(this.invalidClass);
34071 this.fireEvent('invalid', this, msg);
34075 setValue : function(v, suppressEvent)
34077 if(this.value === v){
34084 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34087 Roo.each(this.radioes, function(i){
34089 i.el.removeClass('checked');
34092 Roo.each(this.radioes, function(i){
34094 if(i.value === v || i.value.toString() === v.toString()){
34096 i.el.addClass('checked');
34098 if(suppressEvent !== true){
34099 this.fireEvent('check', this, i);
34110 clearInvalid : function(){
34112 if(!this.el || this.preventMark){
34116 this.el.removeClass([this.invalidClass]);
34118 this.fireEvent('valid', this);
34123 Roo.apply(Roo.bootstrap.RadioSet, {
34127 register : function(set)
34129 this.groups[set.name] = set;
34132 get: function(name)
34134 if (typeof(this.groups[name]) == 'undefined') {
34138 return this.groups[name] ;
34144 * Ext JS Library 1.1.1
34145 * Copyright(c) 2006-2007, Ext JS, LLC.
34147 * Originally Released Under LGPL - original licence link has changed is not relivant.
34150 * <script type="text/javascript">
34155 * @class Roo.bootstrap.SplitBar
34156 * @extends Roo.util.Observable
34157 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34161 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34162 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34163 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34164 split.minSize = 100;
34165 split.maxSize = 600;
34166 split.animate = true;
34167 split.on('moved', splitterMoved);
34170 * Create a new SplitBar
34171 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34172 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34173 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34174 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34175 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34176 position of the SplitBar).
34178 Roo.bootstrap.SplitBar = function(cfg){
34183 // dragElement : elm
34184 // resizingElement: el,
34186 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34187 // placement : Roo.bootstrap.SplitBar.LEFT ,
34188 // existingProxy ???
34191 this.el = Roo.get(cfg.dragElement, true);
34192 this.el.dom.unselectable = "on";
34194 this.resizingEl = Roo.get(cfg.resizingElement, true);
34198 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34199 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34202 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34205 * The minimum size of the resizing element. (Defaults to 0)
34211 * The maximum size of the resizing element. (Defaults to 2000)
34214 this.maxSize = 2000;
34217 * Whether to animate the transition to the new size
34220 this.animate = false;
34223 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34226 this.useShim = false;
34231 if(!cfg.existingProxy){
34233 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34235 this.proxy = Roo.get(cfg.existingProxy).dom;
34238 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34241 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34244 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34247 this.dragSpecs = {};
34250 * @private The adapter to use to positon and resize elements
34252 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34253 this.adapter.init(this);
34255 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34257 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34258 this.el.addClass("roo-splitbar-h");
34261 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34262 this.el.addClass("roo-splitbar-v");
34268 * Fires when the splitter is moved (alias for {@link #event-moved})
34269 * @param {Roo.bootstrap.SplitBar} this
34270 * @param {Number} newSize the new width or height
34275 * Fires when the splitter is moved
34276 * @param {Roo.bootstrap.SplitBar} this
34277 * @param {Number} newSize the new width or height
34281 * @event beforeresize
34282 * Fires before the splitter is dragged
34283 * @param {Roo.bootstrap.SplitBar} this
34285 "beforeresize" : true,
34287 "beforeapply" : true
34290 Roo.util.Observable.call(this);
34293 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34294 onStartProxyDrag : function(x, y){
34295 this.fireEvent("beforeresize", this);
34297 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34299 o.enableDisplayMode("block");
34300 // all splitbars share the same overlay
34301 Roo.bootstrap.SplitBar.prototype.overlay = o;
34303 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34304 this.overlay.show();
34305 Roo.get(this.proxy).setDisplayed("block");
34306 var size = this.adapter.getElementSize(this);
34307 this.activeMinSize = this.getMinimumSize();;
34308 this.activeMaxSize = this.getMaximumSize();;
34309 var c1 = size - this.activeMinSize;
34310 var c2 = Math.max(this.activeMaxSize - size, 0);
34311 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34312 this.dd.resetConstraints();
34313 this.dd.setXConstraint(
34314 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34315 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34317 this.dd.setYConstraint(0, 0);
34319 this.dd.resetConstraints();
34320 this.dd.setXConstraint(0, 0);
34321 this.dd.setYConstraint(
34322 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34323 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34326 this.dragSpecs.startSize = size;
34327 this.dragSpecs.startPoint = [x, y];
34328 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34332 * @private Called after the drag operation by the DDProxy
34334 onEndProxyDrag : function(e){
34335 Roo.get(this.proxy).setDisplayed(false);
34336 var endPoint = Roo.lib.Event.getXY(e);
34338 this.overlay.hide();
34341 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34342 newSize = this.dragSpecs.startSize +
34343 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34344 endPoint[0] - this.dragSpecs.startPoint[0] :
34345 this.dragSpecs.startPoint[0] - endPoint[0]
34348 newSize = this.dragSpecs.startSize +
34349 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34350 endPoint[1] - this.dragSpecs.startPoint[1] :
34351 this.dragSpecs.startPoint[1] - endPoint[1]
34354 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34355 if(newSize != this.dragSpecs.startSize){
34356 if(this.fireEvent('beforeapply', this, newSize) !== false){
34357 this.adapter.setElementSize(this, newSize);
34358 this.fireEvent("moved", this, newSize);
34359 this.fireEvent("resize", this, newSize);
34365 * Get the adapter this SplitBar uses
34366 * @return The adapter object
34368 getAdapter : function(){
34369 return this.adapter;
34373 * Set the adapter this SplitBar uses
34374 * @param {Object} adapter A SplitBar adapter object
34376 setAdapter : function(adapter){
34377 this.adapter = adapter;
34378 this.adapter.init(this);
34382 * Gets the minimum size for the resizing element
34383 * @return {Number} The minimum size
34385 getMinimumSize : function(){
34386 return this.minSize;
34390 * Sets the minimum size for the resizing element
34391 * @param {Number} minSize The minimum size
34393 setMinimumSize : function(minSize){
34394 this.minSize = minSize;
34398 * Gets the maximum size for the resizing element
34399 * @return {Number} The maximum size
34401 getMaximumSize : function(){
34402 return this.maxSize;
34406 * Sets the maximum size for the resizing element
34407 * @param {Number} maxSize The maximum size
34409 setMaximumSize : function(maxSize){
34410 this.maxSize = maxSize;
34414 * Sets the initialize size for the resizing element
34415 * @param {Number} size The initial size
34417 setCurrentSize : function(size){
34418 var oldAnimate = this.animate;
34419 this.animate = false;
34420 this.adapter.setElementSize(this, size);
34421 this.animate = oldAnimate;
34425 * Destroy this splitbar.
34426 * @param {Boolean} removeEl True to remove the element
34428 destroy : function(removeEl){
34430 this.shim.remove();
34433 this.proxy.parentNode.removeChild(this.proxy);
34441 * @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.
34443 Roo.bootstrap.SplitBar.createProxy = function(dir){
34444 var proxy = new Roo.Element(document.createElement("div"));
34445 proxy.unselectable();
34446 var cls = 'roo-splitbar-proxy';
34447 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34448 document.body.appendChild(proxy.dom);
34453 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34454 * Default Adapter. It assumes the splitter and resizing element are not positioned
34455 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34457 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34460 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34461 // do nothing for now
34462 init : function(s){
34466 * Called before drag operations to get the current size of the resizing element.
34467 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34469 getElementSize : function(s){
34470 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34471 return s.resizingEl.getWidth();
34473 return s.resizingEl.getHeight();
34478 * Called after drag operations to set the size of the resizing element.
34479 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34480 * @param {Number} newSize The new size to set
34481 * @param {Function} onComplete A function to be invoked when resizing is complete
34483 setElementSize : function(s, newSize, onComplete){
34484 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34486 s.resizingEl.setWidth(newSize);
34488 onComplete(s, newSize);
34491 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34496 s.resizingEl.setHeight(newSize);
34498 onComplete(s, newSize);
34501 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34508 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34509 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34510 * Adapter that moves the splitter element to align with the resized sizing element.
34511 * Used with an absolute positioned SplitBar.
34512 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34513 * document.body, make sure you assign an id to the body element.
34515 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34516 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34517 this.container = Roo.get(container);
34520 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34521 init : function(s){
34522 this.basic.init(s);
34525 getElementSize : function(s){
34526 return this.basic.getElementSize(s);
34529 setElementSize : function(s, newSize, onComplete){
34530 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34533 moveSplitter : function(s){
34534 var yes = Roo.bootstrap.SplitBar;
34535 switch(s.placement){
34537 s.el.setX(s.resizingEl.getRight());
34540 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34543 s.el.setY(s.resizingEl.getBottom());
34546 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34553 * Orientation constant - Create a vertical SplitBar
34557 Roo.bootstrap.SplitBar.VERTICAL = 1;
34560 * Orientation constant - Create a horizontal SplitBar
34564 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34567 * Placement constant - The resizing element is to the left of the splitter element
34571 Roo.bootstrap.SplitBar.LEFT = 1;
34574 * Placement constant - The resizing element is to the right of the splitter element
34578 Roo.bootstrap.SplitBar.RIGHT = 2;
34581 * Placement constant - The resizing element is positioned above the splitter element
34585 Roo.bootstrap.SplitBar.TOP = 3;
34588 * Placement constant - The resizing element is positioned under splitter element
34592 Roo.bootstrap.SplitBar.BOTTOM = 4;
34593 Roo.namespace("Roo.bootstrap.layout");/*
34595 * Ext JS Library 1.1.1
34596 * Copyright(c) 2006-2007, Ext JS, LLC.
34598 * Originally Released Under LGPL - original licence link has changed is not relivant.
34601 * <script type="text/javascript">
34605 * @class Roo.bootstrap.layout.Manager
34606 * @extends Roo.bootstrap.Component
34607 * Base class for layout managers.
34609 Roo.bootstrap.layout.Manager = function(config)
34611 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34617 /** false to disable window resize monitoring @type Boolean */
34618 this.monitorWindowResize = true;
34623 * Fires when a layout is performed.
34624 * @param {Roo.LayoutManager} this
34628 * @event regionresized
34629 * Fires when the user resizes a region.
34630 * @param {Roo.LayoutRegion} region The resized region
34631 * @param {Number} newSize The new size (width for east/west, height for north/south)
34633 "regionresized" : true,
34635 * @event regioncollapsed
34636 * Fires when a region is collapsed.
34637 * @param {Roo.LayoutRegion} region The collapsed region
34639 "regioncollapsed" : true,
34641 * @event regionexpanded
34642 * Fires when a region is expanded.
34643 * @param {Roo.LayoutRegion} region The expanded region
34645 "regionexpanded" : true
34647 this.updating = false;
34650 this.el = Roo.get(config.el);
34656 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34661 monitorWindowResize : true,
34667 onRender : function(ct, position)
34670 this.el = Roo.get(ct);
34673 //this.fireEvent('render',this);
34677 initEvents: function()
34681 // ie scrollbar fix
34682 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34683 document.body.scroll = "no";
34684 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34685 this.el.position('relative');
34687 this.id = this.el.id;
34688 this.el.addClass("roo-layout-container");
34689 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34690 if(this.el.dom != document.body ) {
34691 this.el.on('resize', this.layout,this);
34692 this.el.on('show', this.layout,this);
34698 * Returns true if this layout is currently being updated
34699 * @return {Boolean}
34701 isUpdating : function(){
34702 return this.updating;
34706 * Suspend the LayoutManager from doing auto-layouts while
34707 * making multiple add or remove calls
34709 beginUpdate : function(){
34710 this.updating = true;
34714 * Restore auto-layouts and optionally disable the manager from performing a layout
34715 * @param {Boolean} noLayout true to disable a layout update
34717 endUpdate : function(noLayout){
34718 this.updating = false;
34724 layout: function(){
34728 onRegionResized : function(region, newSize){
34729 this.fireEvent("regionresized", region, newSize);
34733 onRegionCollapsed : function(region){
34734 this.fireEvent("regioncollapsed", region);
34737 onRegionExpanded : function(region){
34738 this.fireEvent("regionexpanded", region);
34742 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34743 * performs box-model adjustments.
34744 * @return {Object} The size as an object {width: (the width), height: (the height)}
34746 getViewSize : function()
34749 if(this.el.dom != document.body){
34750 size = this.el.getSize();
34752 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34754 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34755 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34760 * Returns the Element this layout is bound to.
34761 * @return {Roo.Element}
34763 getEl : function(){
34768 * Returns the specified region.
34769 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34770 * @return {Roo.LayoutRegion}
34772 getRegion : function(target){
34773 return this.regions[target.toLowerCase()];
34776 onWindowResize : function(){
34777 if(this.monitorWindowResize){
34784 * Ext JS Library 1.1.1
34785 * Copyright(c) 2006-2007, Ext JS, LLC.
34787 * Originally Released Under LGPL - original licence link has changed is not relivant.
34790 * <script type="text/javascript">
34793 * @class Roo.bootstrap.layout.Border
34794 * @extends Roo.bootstrap.layout.Manager
34795 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34796 * please see: examples/bootstrap/nested.html<br><br>
34798 <b>The container the layout is rendered into can be either the body element or any other element.
34799 If it is not the body element, the container needs to either be an absolute positioned element,
34800 or you will need to add "position:relative" to the css of the container. You will also need to specify
34801 the container size if it is not the body element.</b>
34804 * Create a new Border
34805 * @param {Object} config Configuration options
34807 Roo.bootstrap.layout.Border = function(config){
34808 config = config || {};
34809 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34813 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34814 if(config[region]){
34815 config[region].region = region;
34816 this.addRegion(config[region]);
34822 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34824 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34826 * Creates and adds a new region if it doesn't already exist.
34827 * @param {String} target The target region key (north, south, east, west or center).
34828 * @param {Object} config The regions config object
34829 * @return {BorderLayoutRegion} The new region
34831 addRegion : function(config)
34833 if(!this.regions[config.region]){
34834 var r = this.factory(config);
34835 this.bindRegion(r);
34837 return this.regions[config.region];
34841 bindRegion : function(r){
34842 this.regions[r.config.region] = r;
34844 r.on("visibilitychange", this.layout, this);
34845 r.on("paneladded", this.layout, this);
34846 r.on("panelremoved", this.layout, this);
34847 r.on("invalidated", this.layout, this);
34848 r.on("resized", this.onRegionResized, this);
34849 r.on("collapsed", this.onRegionCollapsed, this);
34850 r.on("expanded", this.onRegionExpanded, this);
34854 * Performs a layout update.
34856 layout : function()
34858 if(this.updating) {
34862 // render all the rebions if they have not been done alreayd?
34863 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34864 if(this.regions[region] && !this.regions[region].bodyEl){
34865 this.regions[region].onRender(this.el)
34869 var size = this.getViewSize();
34870 var w = size.width;
34871 var h = size.height;
34876 //var x = 0, y = 0;
34878 var rs = this.regions;
34879 var north = rs["north"];
34880 var south = rs["south"];
34881 var west = rs["west"];
34882 var east = rs["east"];
34883 var center = rs["center"];
34884 //if(this.hideOnLayout){ // not supported anymore
34885 //c.el.setStyle("display", "none");
34887 if(north && north.isVisible()){
34888 var b = north.getBox();
34889 var m = north.getMargins();
34890 b.width = w - (m.left+m.right);
34893 centerY = b.height + b.y + m.bottom;
34894 centerH -= centerY;
34895 north.updateBox(this.safeBox(b));
34897 if(south && south.isVisible()){
34898 var b = south.getBox();
34899 var m = south.getMargins();
34900 b.width = w - (m.left+m.right);
34902 var totalHeight = (b.height + m.top + m.bottom);
34903 b.y = h - totalHeight + m.top;
34904 centerH -= totalHeight;
34905 south.updateBox(this.safeBox(b));
34907 if(west && west.isVisible()){
34908 var b = west.getBox();
34909 var m = west.getMargins();
34910 b.height = centerH - (m.top+m.bottom);
34912 b.y = centerY + m.top;
34913 var totalWidth = (b.width + m.left + m.right);
34914 centerX += totalWidth;
34915 centerW -= totalWidth;
34916 west.updateBox(this.safeBox(b));
34918 if(east && east.isVisible()){
34919 var b = east.getBox();
34920 var m = east.getMargins();
34921 b.height = centerH - (m.top+m.bottom);
34922 var totalWidth = (b.width + m.left + m.right);
34923 b.x = w - totalWidth + m.left;
34924 b.y = centerY + m.top;
34925 centerW -= totalWidth;
34926 east.updateBox(this.safeBox(b));
34929 var m = center.getMargins();
34931 x: centerX + m.left,
34932 y: centerY + m.top,
34933 width: centerW - (m.left+m.right),
34934 height: centerH - (m.top+m.bottom)
34936 //if(this.hideOnLayout){
34937 //center.el.setStyle("display", "block");
34939 center.updateBox(this.safeBox(centerBox));
34942 this.fireEvent("layout", this);
34946 safeBox : function(box){
34947 box.width = Math.max(0, box.width);
34948 box.height = Math.max(0, box.height);
34953 * Adds a ContentPanel (or subclass) to this layout.
34954 * @param {String} target The target region key (north, south, east, west or center).
34955 * @param {Roo.ContentPanel} panel The panel to add
34956 * @return {Roo.ContentPanel} The added panel
34958 add : function(target, panel){
34960 target = target.toLowerCase();
34961 return this.regions[target].add(panel);
34965 * Remove a ContentPanel (or subclass) to this layout.
34966 * @param {String} target The target region key (north, south, east, west or center).
34967 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34968 * @return {Roo.ContentPanel} The removed panel
34970 remove : function(target, panel){
34971 target = target.toLowerCase();
34972 return this.regions[target].remove(panel);
34976 * Searches all regions for a panel with the specified id
34977 * @param {String} panelId
34978 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34980 findPanel : function(panelId){
34981 var rs = this.regions;
34982 for(var target in rs){
34983 if(typeof rs[target] != "function"){
34984 var p = rs[target].getPanel(panelId);
34994 * Searches all regions for a panel with the specified id and activates (shows) it.
34995 * @param {String/ContentPanel} panelId The panels id or the panel itself
34996 * @return {Roo.ContentPanel} The shown panel or null
34998 showPanel : function(panelId) {
34999 var rs = this.regions;
35000 for(var target in rs){
35001 var r = rs[target];
35002 if(typeof r != "function"){
35003 if(r.hasPanel(panelId)){
35004 return r.showPanel(panelId);
35012 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35013 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35016 restoreState : function(provider){
35018 provider = Roo.state.Manager;
35020 var sm = new Roo.LayoutStateManager();
35021 sm.init(this, provider);
35027 * Adds a xtype elements to the layout.
35031 xtype : 'ContentPanel',
35038 xtype : 'NestedLayoutPanel',
35044 items : [ ... list of content panels or nested layout panels.. ]
35048 * @param {Object} cfg Xtype definition of item to add.
35050 addxtype : function(cfg)
35052 // basically accepts a pannel...
35053 // can accept a layout region..!?!?
35054 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35057 // theory? children can only be panels??
35059 //if (!cfg.xtype.match(/Panel$/)) {
35064 if (typeof(cfg.region) == 'undefined') {
35065 Roo.log("Failed to add Panel, region was not set");
35069 var region = cfg.region;
35075 xitems = cfg.items;
35082 case 'Content': // ContentPanel (el, cfg)
35083 case 'Scroll': // ContentPanel (el, cfg)
35085 cfg.autoCreate = true;
35086 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35088 // var el = this.el.createChild();
35089 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35092 this.add(region, ret);
35096 case 'TreePanel': // our new panel!
35097 cfg.el = this.el.createChild();
35098 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35099 this.add(region, ret);
35104 // create a new Layout (which is a Border Layout...
35106 var clayout = cfg.layout;
35107 clayout.el = this.el.createChild();
35108 clayout.items = clayout.items || [];
35112 // replace this exitems with the clayout ones..
35113 xitems = clayout.items;
35115 // force background off if it's in center...
35116 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35117 cfg.background = false;
35119 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35122 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35123 //console.log('adding nested layout panel ' + cfg.toSource());
35124 this.add(region, ret);
35125 nb = {}; /// find first...
35130 // needs grid and region
35132 //var el = this.getRegion(region).el.createChild();
35134 *var el = this.el.createChild();
35135 // create the grid first...
35136 cfg.grid.container = el;
35137 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35140 if (region == 'center' && this.active ) {
35141 cfg.background = false;
35144 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35146 this.add(region, ret);
35148 if (cfg.background) {
35149 // render grid on panel activation (if panel background)
35150 ret.on('activate', function(gp) {
35151 if (!gp.grid.rendered) {
35152 // gp.grid.render(el);
35156 // cfg.grid.render(el);
35162 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35163 // it was the old xcomponent building that caused this before.
35164 // espeically if border is the top element in the tree.
35174 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35176 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35177 this.add(region, ret);
35181 throw "Can not add '" + cfg.xtype + "' to Border";
35187 this.beginUpdate();
35191 Roo.each(xitems, function(i) {
35192 region = nb && i.region ? i.region : false;
35194 var add = ret.addxtype(i);
35197 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35198 if (!i.background) {
35199 abn[region] = nb[region] ;
35206 // make the last non-background panel active..
35207 //if (nb) { Roo.log(abn); }
35210 for(var r in abn) {
35211 region = this.getRegion(r);
35213 // tried using nb[r], but it does not work..
35215 region.showPanel(abn[r]);
35226 factory : function(cfg)
35229 var validRegions = Roo.bootstrap.layout.Border.regions;
35231 var target = cfg.region;
35234 var r = Roo.bootstrap.layout;
35238 return new r.North(cfg);
35240 return new r.South(cfg);
35242 return new r.East(cfg);
35244 return new r.West(cfg);
35246 return new r.Center(cfg);
35248 throw 'Layout region "'+target+'" not supported.';
35255 * Ext JS Library 1.1.1
35256 * Copyright(c) 2006-2007, Ext JS, LLC.
35258 * Originally Released Under LGPL - original licence link has changed is not relivant.
35261 * <script type="text/javascript">
35265 * @class Roo.bootstrap.layout.Basic
35266 * @extends Roo.util.Observable
35267 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35268 * and does not have a titlebar, tabs or any other features. All it does is size and position
35269 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35270 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35271 * @cfg {string} region the region that it inhabits..
35272 * @cfg {bool} skipConfig skip config?
35276 Roo.bootstrap.layout.Basic = function(config){
35278 this.mgr = config.mgr;
35280 this.position = config.region;
35282 var skipConfig = config.skipConfig;
35286 * @scope Roo.BasicLayoutRegion
35290 * @event beforeremove
35291 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35292 * @param {Roo.LayoutRegion} this
35293 * @param {Roo.ContentPanel} panel The panel
35294 * @param {Object} e The cancel event object
35296 "beforeremove" : true,
35298 * @event invalidated
35299 * Fires when the layout for this region is changed.
35300 * @param {Roo.LayoutRegion} this
35302 "invalidated" : true,
35304 * @event visibilitychange
35305 * Fires when this region is shown or hidden
35306 * @param {Roo.LayoutRegion} this
35307 * @param {Boolean} visibility true or false
35309 "visibilitychange" : true,
35311 * @event paneladded
35312 * Fires when a panel is added.
35313 * @param {Roo.LayoutRegion} this
35314 * @param {Roo.ContentPanel} panel The panel
35316 "paneladded" : true,
35318 * @event panelremoved
35319 * Fires when a panel is removed.
35320 * @param {Roo.LayoutRegion} this
35321 * @param {Roo.ContentPanel} panel The panel
35323 "panelremoved" : true,
35325 * @event beforecollapse
35326 * Fires when this region before collapse.
35327 * @param {Roo.LayoutRegion} this
35329 "beforecollapse" : true,
35332 * Fires when this region is collapsed.
35333 * @param {Roo.LayoutRegion} this
35335 "collapsed" : true,
35338 * Fires when this region is expanded.
35339 * @param {Roo.LayoutRegion} this
35344 * Fires when this region is slid into view.
35345 * @param {Roo.LayoutRegion} this
35347 "slideshow" : true,
35350 * Fires when this region slides out of view.
35351 * @param {Roo.LayoutRegion} this
35353 "slidehide" : true,
35355 * @event panelactivated
35356 * Fires when a panel is activated.
35357 * @param {Roo.LayoutRegion} this
35358 * @param {Roo.ContentPanel} panel The activated panel
35360 "panelactivated" : true,
35363 * Fires when the user resizes this region.
35364 * @param {Roo.LayoutRegion} this
35365 * @param {Number} newSize The new size (width for east/west, height for north/south)
35369 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35370 this.panels = new Roo.util.MixedCollection();
35371 this.panels.getKey = this.getPanelId.createDelegate(this);
35373 this.activePanel = null;
35374 // ensure listeners are added...
35376 if (config.listeners || config.events) {
35377 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35378 listeners : config.listeners || {},
35379 events : config.events || {}
35383 if(skipConfig !== true){
35384 this.applyConfig(config);
35388 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35390 getPanelId : function(p){
35394 applyConfig : function(config){
35395 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35396 this.config = config;
35401 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35402 * the width, for horizontal (north, south) the height.
35403 * @param {Number} newSize The new width or height
35405 resizeTo : function(newSize){
35406 var el = this.el ? this.el :
35407 (this.activePanel ? this.activePanel.getEl() : null);
35409 switch(this.position){
35412 el.setWidth(newSize);
35413 this.fireEvent("resized", this, newSize);
35417 el.setHeight(newSize);
35418 this.fireEvent("resized", this, newSize);
35424 getBox : function(){
35425 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35428 getMargins : function(){
35429 return this.margins;
35432 updateBox : function(box){
35434 var el = this.activePanel.getEl();
35435 el.dom.style.left = box.x + "px";
35436 el.dom.style.top = box.y + "px";
35437 this.activePanel.setSize(box.width, box.height);
35441 * Returns the container element for this region.
35442 * @return {Roo.Element}
35444 getEl : function(){
35445 return this.activePanel;
35449 * Returns true if this region is currently visible.
35450 * @return {Boolean}
35452 isVisible : function(){
35453 return this.activePanel ? true : false;
35456 setActivePanel : function(panel){
35457 panel = this.getPanel(panel);
35458 if(this.activePanel && this.activePanel != panel){
35459 this.activePanel.setActiveState(false);
35460 this.activePanel.getEl().setLeftTop(-10000,-10000);
35462 this.activePanel = panel;
35463 panel.setActiveState(true);
35465 panel.setSize(this.box.width, this.box.height);
35467 this.fireEvent("panelactivated", this, panel);
35468 this.fireEvent("invalidated");
35472 * Show the specified panel.
35473 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35474 * @return {Roo.ContentPanel} The shown panel or null
35476 showPanel : function(panel){
35477 panel = this.getPanel(panel);
35479 this.setActivePanel(panel);
35485 * Get the active panel for this region.
35486 * @return {Roo.ContentPanel} The active panel or null
35488 getActivePanel : function(){
35489 return this.activePanel;
35493 * Add the passed ContentPanel(s)
35494 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35495 * @return {Roo.ContentPanel} The panel added (if only one was added)
35497 add : function(panel){
35498 if(arguments.length > 1){
35499 for(var i = 0, len = arguments.length; i < len; i++) {
35500 this.add(arguments[i]);
35504 if(this.hasPanel(panel)){
35505 this.showPanel(panel);
35508 var el = panel.getEl();
35509 if(el.dom.parentNode != this.mgr.el.dom){
35510 this.mgr.el.dom.appendChild(el.dom);
35512 if(panel.setRegion){
35513 panel.setRegion(this);
35515 this.panels.add(panel);
35516 el.setStyle("position", "absolute");
35517 if(!panel.background){
35518 this.setActivePanel(panel);
35519 if(this.config.initialSize && this.panels.getCount()==1){
35520 this.resizeTo(this.config.initialSize);
35523 this.fireEvent("paneladded", this, panel);
35528 * Returns true if the panel is in this region.
35529 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35530 * @return {Boolean}
35532 hasPanel : function(panel){
35533 if(typeof panel == "object"){ // must be panel obj
35534 panel = panel.getId();
35536 return this.getPanel(panel) ? true : false;
35540 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35541 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35542 * @param {Boolean} preservePanel Overrides the config preservePanel option
35543 * @return {Roo.ContentPanel} The panel that was removed
35545 remove : function(panel, preservePanel){
35546 panel = this.getPanel(panel);
35551 this.fireEvent("beforeremove", this, panel, e);
35552 if(e.cancel === true){
35555 var panelId = panel.getId();
35556 this.panels.removeKey(panelId);
35561 * Returns the panel specified or null if it's not in this region.
35562 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35563 * @return {Roo.ContentPanel}
35565 getPanel : function(id){
35566 if(typeof id == "object"){ // must be panel obj
35569 return this.panels.get(id);
35573 * Returns this regions position (north/south/east/west/center).
35576 getPosition: function(){
35577 return this.position;
35581 * Ext JS Library 1.1.1
35582 * Copyright(c) 2006-2007, Ext JS, LLC.
35584 * Originally Released Under LGPL - original licence link has changed is not relivant.
35587 * <script type="text/javascript">
35591 * @class Roo.bootstrap.layout.Region
35592 * @extends Roo.bootstrap.layout.Basic
35593 * This class represents a region in a layout manager.
35595 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35596 * @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})
35597 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35598 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35599 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35600 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35601 * @cfg {String} title The title for the region (overrides panel titles)
35602 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35603 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35604 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35605 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35606 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35607 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35608 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35609 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35610 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35611 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35613 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35614 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35615 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35616 * @cfg {Number} width For East/West panels
35617 * @cfg {Number} height For North/South panels
35618 * @cfg {Boolean} split To show the splitter
35619 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35621 * @cfg {string} cls Extra CSS classes to add to region
35623 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35624 * @cfg {string} region the region that it inhabits..
35627 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35628 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35630 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35631 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35632 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35634 Roo.bootstrap.layout.Region = function(config)
35636 this.applyConfig(config);
35638 var mgr = config.mgr;
35639 var pos = config.region;
35640 config.skipConfig = true;
35641 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35644 this.onRender(mgr.el);
35647 this.visible = true;
35648 this.collapsed = false;
35649 this.unrendered_panels = [];
35652 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35654 position: '', // set by wrapper (eg. north/south etc..)
35655 unrendered_panels : null, // unrendered panels.
35656 createBody : function(){
35657 /** This region's body element
35658 * @type Roo.Element */
35659 this.bodyEl = this.el.createChild({
35661 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35665 onRender: function(ctr, pos)
35667 var dh = Roo.DomHelper;
35668 /** This region's container element
35669 * @type Roo.Element */
35670 this.el = dh.append(ctr.dom, {
35672 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35674 /** This region's title element
35675 * @type Roo.Element */
35677 this.titleEl = dh.append(this.el.dom,
35680 unselectable: "on",
35681 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35683 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35684 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35687 this.titleEl.enableDisplayMode();
35688 /** This region's title text element
35689 * @type HTMLElement */
35690 this.titleTextEl = this.titleEl.dom.firstChild;
35691 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35693 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35694 this.closeBtn.enableDisplayMode();
35695 this.closeBtn.on("click", this.closeClicked, this);
35696 this.closeBtn.hide();
35698 this.createBody(this.config);
35699 if(this.config.hideWhenEmpty){
35701 this.on("paneladded", this.validateVisibility, this);
35702 this.on("panelremoved", this.validateVisibility, this);
35704 if(this.autoScroll){
35705 this.bodyEl.setStyle("overflow", "auto");
35707 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35709 //if(c.titlebar !== false){
35710 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35711 this.titleEl.hide();
35713 this.titleEl.show();
35714 if(this.config.title){
35715 this.titleTextEl.innerHTML = this.config.title;
35719 if(this.config.collapsed){
35720 this.collapse(true);
35722 if(this.config.hidden){
35726 if (this.unrendered_panels && this.unrendered_panels.length) {
35727 for (var i =0;i< this.unrendered_panels.length; i++) {
35728 this.add(this.unrendered_panels[i]);
35730 this.unrendered_panels = null;
35736 applyConfig : function(c)
35739 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35740 var dh = Roo.DomHelper;
35741 if(c.titlebar !== false){
35742 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35743 this.collapseBtn.on("click", this.collapse, this);
35744 this.collapseBtn.enableDisplayMode();
35746 if(c.showPin === true || this.showPin){
35747 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35748 this.stickBtn.enableDisplayMode();
35749 this.stickBtn.on("click", this.expand, this);
35750 this.stickBtn.hide();
35755 /** This region's collapsed element
35756 * @type Roo.Element */
35759 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35760 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35763 if(c.floatable !== false){
35764 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35765 this.collapsedEl.on("click", this.collapseClick, this);
35768 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35769 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35770 id: "message", unselectable: "on", style:{"float":"left"}});
35771 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35773 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35774 this.expandBtn.on("click", this.expand, this);
35778 if(this.collapseBtn){
35779 this.collapseBtn.setVisible(c.collapsible == true);
35782 this.cmargins = c.cmargins || this.cmargins ||
35783 (this.position == "west" || this.position == "east" ?
35784 {top: 0, left: 2, right:2, bottom: 0} :
35785 {top: 2, left: 0, right:0, bottom: 2});
35787 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35790 this.bottomTabs = c.tabPosition != "top";
35792 this.autoScroll = c.autoScroll || false;
35797 this.duration = c.duration || .30;
35798 this.slideDuration = c.slideDuration || .45;
35803 * Returns true if this region is currently visible.
35804 * @return {Boolean}
35806 isVisible : function(){
35807 return this.visible;
35811 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35812 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35814 //setCollapsedTitle : function(title){
35815 // title = title || " ";
35816 // if(this.collapsedTitleTextEl){
35817 // this.collapsedTitleTextEl.innerHTML = title;
35821 getBox : function(){
35823 // if(!this.collapsed){
35824 b = this.el.getBox(false, true);
35826 // b = this.collapsedEl.getBox(false, true);
35831 getMargins : function(){
35832 return this.margins;
35833 //return this.collapsed ? this.cmargins : this.margins;
35836 highlight : function(){
35837 this.el.addClass("x-layout-panel-dragover");
35840 unhighlight : function(){
35841 this.el.removeClass("x-layout-panel-dragover");
35844 updateBox : function(box)
35846 if (!this.bodyEl) {
35847 return; // not rendered yet..
35851 if(!this.collapsed){
35852 this.el.dom.style.left = box.x + "px";
35853 this.el.dom.style.top = box.y + "px";
35854 this.updateBody(box.width, box.height);
35856 this.collapsedEl.dom.style.left = box.x + "px";
35857 this.collapsedEl.dom.style.top = box.y + "px";
35858 this.collapsedEl.setSize(box.width, box.height);
35861 this.tabs.autoSizeTabs();
35865 updateBody : function(w, h)
35868 this.el.setWidth(w);
35869 w -= this.el.getBorderWidth("rl");
35870 if(this.config.adjustments){
35871 w += this.config.adjustments[0];
35874 if(h !== null && h > 0){
35875 this.el.setHeight(h);
35876 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35877 h -= this.el.getBorderWidth("tb");
35878 if(this.config.adjustments){
35879 h += this.config.adjustments[1];
35881 this.bodyEl.setHeight(h);
35883 h = this.tabs.syncHeight(h);
35886 if(this.panelSize){
35887 w = w !== null ? w : this.panelSize.width;
35888 h = h !== null ? h : this.panelSize.height;
35890 if(this.activePanel){
35891 var el = this.activePanel.getEl();
35892 w = w !== null ? w : el.getWidth();
35893 h = h !== null ? h : el.getHeight();
35894 this.panelSize = {width: w, height: h};
35895 this.activePanel.setSize(w, h);
35897 if(Roo.isIE && this.tabs){
35898 this.tabs.el.repaint();
35903 * Returns the container element for this region.
35904 * @return {Roo.Element}
35906 getEl : function(){
35911 * Hides this region.
35914 //if(!this.collapsed){
35915 this.el.dom.style.left = "-2000px";
35918 // this.collapsedEl.dom.style.left = "-2000px";
35919 // this.collapsedEl.hide();
35921 this.visible = false;
35922 this.fireEvent("visibilitychange", this, false);
35926 * Shows this region if it was previously hidden.
35929 //if(!this.collapsed){
35932 // this.collapsedEl.show();
35934 this.visible = true;
35935 this.fireEvent("visibilitychange", this, true);
35938 closeClicked : function(){
35939 if(this.activePanel){
35940 this.remove(this.activePanel);
35944 collapseClick : function(e){
35946 e.stopPropagation();
35949 e.stopPropagation();
35955 * Collapses this region.
35956 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35959 collapse : function(skipAnim, skipCheck = false){
35960 if(this.collapsed) {
35964 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35966 this.collapsed = true;
35968 this.split.el.hide();
35970 if(this.config.animate && skipAnim !== true){
35971 this.fireEvent("invalidated", this);
35972 this.animateCollapse();
35974 this.el.setLocation(-20000,-20000);
35976 this.collapsedEl.show();
35977 this.fireEvent("collapsed", this);
35978 this.fireEvent("invalidated", this);
35984 animateCollapse : function(){
35989 * Expands this region if it was previously collapsed.
35990 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35991 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35994 expand : function(e, skipAnim){
35996 e.stopPropagation();
35998 if(!this.collapsed || this.el.hasActiveFx()) {
36002 this.afterSlideIn();
36005 this.collapsed = false;
36006 if(this.config.animate && skipAnim !== true){
36007 this.animateExpand();
36011 this.split.el.show();
36013 this.collapsedEl.setLocation(-2000,-2000);
36014 this.collapsedEl.hide();
36015 this.fireEvent("invalidated", this);
36016 this.fireEvent("expanded", this);
36020 animateExpand : function(){
36024 initTabs : function()
36026 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36028 var ts = new Roo.bootstrap.panel.Tabs({
36029 el: this.bodyEl.dom,
36030 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36031 disableTooltips: this.config.disableTabTips,
36032 toolbar : this.config.toolbar
36035 if(this.config.hideTabs){
36036 ts.stripWrap.setDisplayed(false);
36039 ts.resizeTabs = this.config.resizeTabs === true;
36040 ts.minTabWidth = this.config.minTabWidth || 40;
36041 ts.maxTabWidth = this.config.maxTabWidth || 250;
36042 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36043 ts.monitorResize = false;
36044 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36045 ts.bodyEl.addClass('roo-layout-tabs-body');
36046 this.panels.each(this.initPanelAsTab, this);
36049 initPanelAsTab : function(panel){
36050 var ti = this.tabs.addTab(
36054 this.config.closeOnTab && panel.isClosable(),
36057 if(panel.tabTip !== undefined){
36058 ti.setTooltip(panel.tabTip);
36060 ti.on("activate", function(){
36061 this.setActivePanel(panel);
36064 if(this.config.closeOnTab){
36065 ti.on("beforeclose", function(t, e){
36067 this.remove(panel);
36071 panel.tabItem = ti;
36076 updatePanelTitle : function(panel, title)
36078 if(this.activePanel == panel){
36079 this.updateTitle(title);
36082 var ti = this.tabs.getTab(panel.getEl().id);
36084 if(panel.tabTip !== undefined){
36085 ti.setTooltip(panel.tabTip);
36090 updateTitle : function(title){
36091 if(this.titleTextEl && !this.config.title){
36092 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36096 setActivePanel : function(panel)
36098 panel = this.getPanel(panel);
36099 if(this.activePanel && this.activePanel != panel){
36100 if(this.activePanel.setActiveState(false) === false){
36104 this.activePanel = panel;
36105 panel.setActiveState(true);
36106 if(this.panelSize){
36107 panel.setSize(this.panelSize.width, this.panelSize.height);
36110 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36112 this.updateTitle(panel.getTitle());
36114 this.fireEvent("invalidated", this);
36116 this.fireEvent("panelactivated", this, panel);
36120 * Shows the specified panel.
36121 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36122 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36124 showPanel : function(panel)
36126 panel = this.getPanel(panel);
36129 var tab = this.tabs.getTab(panel.getEl().id);
36130 if(tab.isHidden()){
36131 this.tabs.unhideTab(tab.id);
36135 this.setActivePanel(panel);
36142 * Get the active panel for this region.
36143 * @return {Roo.ContentPanel} The active panel or null
36145 getActivePanel : function(){
36146 return this.activePanel;
36149 validateVisibility : function(){
36150 if(this.panels.getCount() < 1){
36151 this.updateTitle(" ");
36152 this.closeBtn.hide();
36155 if(!this.isVisible()){
36162 * Adds the passed ContentPanel(s) to this region.
36163 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36164 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36166 add : function(panel)
36168 if(arguments.length > 1){
36169 for(var i = 0, len = arguments.length; i < len; i++) {
36170 this.add(arguments[i]);
36175 // if we have not been rendered yet, then we can not really do much of this..
36176 if (!this.bodyEl) {
36177 this.unrendered_panels.push(panel);
36184 if(this.hasPanel(panel)){
36185 this.showPanel(panel);
36188 panel.setRegion(this);
36189 this.panels.add(panel);
36190 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36191 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36192 // and hide them... ???
36193 this.bodyEl.dom.appendChild(panel.getEl().dom);
36194 if(panel.background !== true){
36195 this.setActivePanel(panel);
36197 this.fireEvent("paneladded", this, panel);
36204 this.initPanelAsTab(panel);
36208 if(panel.background !== true){
36209 this.tabs.activate(panel.getEl().id);
36211 this.fireEvent("paneladded", this, panel);
36216 * Hides the tab for the specified panel.
36217 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36219 hidePanel : function(panel){
36220 if(this.tabs && (panel = this.getPanel(panel))){
36221 this.tabs.hideTab(panel.getEl().id);
36226 * Unhides the tab for a previously hidden panel.
36227 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36229 unhidePanel : function(panel){
36230 if(this.tabs && (panel = this.getPanel(panel))){
36231 this.tabs.unhideTab(panel.getEl().id);
36235 clearPanels : function(){
36236 while(this.panels.getCount() > 0){
36237 this.remove(this.panels.first());
36242 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36243 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36244 * @param {Boolean} preservePanel Overrides the config preservePanel option
36245 * @return {Roo.ContentPanel} The panel that was removed
36247 remove : function(panel, preservePanel)
36249 panel = this.getPanel(panel);
36254 this.fireEvent("beforeremove", this, panel, e);
36255 if(e.cancel === true){
36258 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36259 var panelId = panel.getId();
36260 this.panels.removeKey(panelId);
36262 document.body.appendChild(panel.getEl().dom);
36265 this.tabs.removeTab(panel.getEl().id);
36266 }else if (!preservePanel){
36267 this.bodyEl.dom.removeChild(panel.getEl().dom);
36269 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36270 var p = this.panels.first();
36271 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36272 tempEl.appendChild(p.getEl().dom);
36273 this.bodyEl.update("");
36274 this.bodyEl.dom.appendChild(p.getEl().dom);
36276 this.updateTitle(p.getTitle());
36278 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36279 this.setActivePanel(p);
36281 panel.setRegion(null);
36282 if(this.activePanel == panel){
36283 this.activePanel = null;
36285 if(this.config.autoDestroy !== false && preservePanel !== true){
36286 try{panel.destroy();}catch(e){}
36288 this.fireEvent("panelremoved", this, panel);
36293 * Returns the TabPanel component used by this region
36294 * @return {Roo.TabPanel}
36296 getTabs : function(){
36300 createTool : function(parentEl, className){
36301 var btn = Roo.DomHelper.append(parentEl, {
36303 cls: "x-layout-tools-button",
36306 cls: "roo-layout-tools-button-inner " + className,
36310 btn.addClassOnOver("roo-layout-tools-button-over");
36315 * Ext JS Library 1.1.1
36316 * Copyright(c) 2006-2007, Ext JS, LLC.
36318 * Originally Released Under LGPL - original licence link has changed is not relivant.
36321 * <script type="text/javascript">
36327 * @class Roo.SplitLayoutRegion
36328 * @extends Roo.LayoutRegion
36329 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36331 Roo.bootstrap.layout.Split = function(config){
36332 this.cursor = config.cursor;
36333 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36336 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36338 splitTip : "Drag to resize.",
36339 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36340 useSplitTips : false,
36342 applyConfig : function(config){
36343 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36346 onRender : function(ctr,pos) {
36348 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36349 if(!this.config.split){
36354 var splitEl = Roo.DomHelper.append(ctr.dom, {
36356 id: this.el.id + "-split",
36357 cls: "roo-layout-split roo-layout-split-"+this.position,
36360 /** The SplitBar for this region
36361 * @type Roo.SplitBar */
36362 // does not exist yet...
36363 Roo.log([this.position, this.orientation]);
36365 this.split = new Roo.bootstrap.SplitBar({
36366 dragElement : splitEl,
36367 resizingElement: this.el,
36368 orientation : this.orientation
36371 this.split.on("moved", this.onSplitMove, this);
36372 this.split.useShim = this.config.useShim === true;
36373 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36374 if(this.useSplitTips){
36375 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36377 //if(config.collapsible){
36378 // this.split.el.on("dblclick", this.collapse, this);
36381 if(typeof this.config.minSize != "undefined"){
36382 this.split.minSize = this.config.minSize;
36384 if(typeof this.config.maxSize != "undefined"){
36385 this.split.maxSize = this.config.maxSize;
36387 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36388 this.hideSplitter();
36393 getHMaxSize : function(){
36394 var cmax = this.config.maxSize || 10000;
36395 var center = this.mgr.getRegion("center");
36396 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36399 getVMaxSize : function(){
36400 var cmax = this.config.maxSize || 10000;
36401 var center = this.mgr.getRegion("center");
36402 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36405 onSplitMove : function(split, newSize){
36406 this.fireEvent("resized", this, newSize);
36410 * Returns the {@link Roo.SplitBar} for this region.
36411 * @return {Roo.SplitBar}
36413 getSplitBar : function(){
36418 this.hideSplitter();
36419 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36422 hideSplitter : function(){
36424 this.split.el.setLocation(-2000,-2000);
36425 this.split.el.hide();
36431 this.split.el.show();
36433 Roo.bootstrap.layout.Split.superclass.show.call(this);
36436 beforeSlide: function(){
36437 if(Roo.isGecko){// firefox overflow auto bug workaround
36438 this.bodyEl.clip();
36440 this.tabs.bodyEl.clip();
36442 if(this.activePanel){
36443 this.activePanel.getEl().clip();
36445 if(this.activePanel.beforeSlide){
36446 this.activePanel.beforeSlide();
36452 afterSlide : function(){
36453 if(Roo.isGecko){// firefox overflow auto bug workaround
36454 this.bodyEl.unclip();
36456 this.tabs.bodyEl.unclip();
36458 if(this.activePanel){
36459 this.activePanel.getEl().unclip();
36460 if(this.activePanel.afterSlide){
36461 this.activePanel.afterSlide();
36467 initAutoHide : function(){
36468 if(this.autoHide !== false){
36469 if(!this.autoHideHd){
36470 var st = new Roo.util.DelayedTask(this.slideIn, this);
36471 this.autoHideHd = {
36472 "mouseout": function(e){
36473 if(!e.within(this.el, true)){
36477 "mouseover" : function(e){
36483 this.el.on(this.autoHideHd);
36487 clearAutoHide : function(){
36488 if(this.autoHide !== false){
36489 this.el.un("mouseout", this.autoHideHd.mouseout);
36490 this.el.un("mouseover", this.autoHideHd.mouseover);
36494 clearMonitor : function(){
36495 Roo.get(document).un("click", this.slideInIf, this);
36498 // these names are backwards but not changed for compat
36499 slideOut : function(){
36500 if(this.isSlid || this.el.hasActiveFx()){
36503 this.isSlid = true;
36504 if(this.collapseBtn){
36505 this.collapseBtn.hide();
36507 this.closeBtnState = this.closeBtn.getStyle('display');
36508 this.closeBtn.hide();
36510 this.stickBtn.show();
36513 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36514 this.beforeSlide();
36515 this.el.setStyle("z-index", 10001);
36516 this.el.slideIn(this.getSlideAnchor(), {
36517 callback: function(){
36519 this.initAutoHide();
36520 Roo.get(document).on("click", this.slideInIf, this);
36521 this.fireEvent("slideshow", this);
36528 afterSlideIn : function(){
36529 this.clearAutoHide();
36530 this.isSlid = false;
36531 this.clearMonitor();
36532 this.el.setStyle("z-index", "");
36533 if(this.collapseBtn){
36534 this.collapseBtn.show();
36536 this.closeBtn.setStyle('display', this.closeBtnState);
36538 this.stickBtn.hide();
36540 this.fireEvent("slidehide", this);
36543 slideIn : function(cb){
36544 if(!this.isSlid || this.el.hasActiveFx()){
36548 this.isSlid = false;
36549 this.beforeSlide();
36550 this.el.slideOut(this.getSlideAnchor(), {
36551 callback: function(){
36552 this.el.setLeftTop(-10000, -10000);
36554 this.afterSlideIn();
36562 slideInIf : function(e){
36563 if(!e.within(this.el)){
36568 animateCollapse : function(){
36569 this.beforeSlide();
36570 this.el.setStyle("z-index", 20000);
36571 var anchor = this.getSlideAnchor();
36572 this.el.slideOut(anchor, {
36573 callback : function(){
36574 this.el.setStyle("z-index", "");
36575 this.collapsedEl.slideIn(anchor, {duration:.3});
36577 this.el.setLocation(-10000,-10000);
36579 this.fireEvent("collapsed", this);
36586 animateExpand : function(){
36587 this.beforeSlide();
36588 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36589 this.el.setStyle("z-index", 20000);
36590 this.collapsedEl.hide({
36593 this.el.slideIn(this.getSlideAnchor(), {
36594 callback : function(){
36595 this.el.setStyle("z-index", "");
36598 this.split.el.show();
36600 this.fireEvent("invalidated", this);
36601 this.fireEvent("expanded", this);
36629 getAnchor : function(){
36630 return this.anchors[this.position];
36633 getCollapseAnchor : function(){
36634 return this.canchors[this.position];
36637 getSlideAnchor : function(){
36638 return this.sanchors[this.position];
36641 getAlignAdj : function(){
36642 var cm = this.cmargins;
36643 switch(this.position){
36659 getExpandAdj : function(){
36660 var c = this.collapsedEl, cm = this.cmargins;
36661 switch(this.position){
36663 return [-(cm.right+c.getWidth()+cm.left), 0];
36666 return [cm.right+c.getWidth()+cm.left, 0];
36669 return [0, -(cm.top+cm.bottom+c.getHeight())];
36672 return [0, cm.top+cm.bottom+c.getHeight()];
36678 * Ext JS Library 1.1.1
36679 * Copyright(c) 2006-2007, Ext JS, LLC.
36681 * Originally Released Under LGPL - original licence link has changed is not relivant.
36684 * <script type="text/javascript">
36687 * These classes are private internal classes
36689 Roo.bootstrap.layout.Center = function(config){
36690 config.region = "center";
36691 Roo.bootstrap.layout.Region.call(this, config);
36692 this.visible = true;
36693 this.minWidth = config.minWidth || 20;
36694 this.minHeight = config.minHeight || 20;
36697 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36699 // center panel can't be hidden
36703 // center panel can't be hidden
36706 getMinWidth: function(){
36707 return this.minWidth;
36710 getMinHeight: function(){
36711 return this.minHeight;
36724 Roo.bootstrap.layout.North = function(config)
36726 config.region = 'north';
36727 config.cursor = 'n-resize';
36729 Roo.bootstrap.layout.Split.call(this, config);
36733 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36734 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36735 this.split.el.addClass("roo-layout-split-v");
36737 var size = config.initialSize || config.height;
36738 if(typeof size != "undefined"){
36739 this.el.setHeight(size);
36742 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36744 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36748 getBox : function(){
36749 if(this.collapsed){
36750 return this.collapsedEl.getBox();
36752 var box = this.el.getBox();
36754 box.height += this.split.el.getHeight();
36759 updateBox : function(box){
36760 if(this.split && !this.collapsed){
36761 box.height -= this.split.el.getHeight();
36762 this.split.el.setLeft(box.x);
36763 this.split.el.setTop(box.y+box.height);
36764 this.split.el.setWidth(box.width);
36766 if(this.collapsed){
36767 this.updateBody(box.width, null);
36769 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36777 Roo.bootstrap.layout.South = function(config){
36778 config.region = 'south';
36779 config.cursor = 's-resize';
36780 Roo.bootstrap.layout.Split.call(this, config);
36782 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36783 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36784 this.split.el.addClass("roo-layout-split-v");
36786 var size = config.initialSize || config.height;
36787 if(typeof size != "undefined"){
36788 this.el.setHeight(size);
36792 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36793 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36794 getBox : function(){
36795 if(this.collapsed){
36796 return this.collapsedEl.getBox();
36798 var box = this.el.getBox();
36800 var sh = this.split.el.getHeight();
36807 updateBox : function(box){
36808 if(this.split && !this.collapsed){
36809 var sh = this.split.el.getHeight();
36812 this.split.el.setLeft(box.x);
36813 this.split.el.setTop(box.y-sh);
36814 this.split.el.setWidth(box.width);
36816 if(this.collapsed){
36817 this.updateBody(box.width, null);
36819 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36823 Roo.bootstrap.layout.East = function(config){
36824 config.region = "east";
36825 config.cursor = "e-resize";
36826 Roo.bootstrap.layout.Split.call(this, config);
36828 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36829 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36830 this.split.el.addClass("roo-layout-split-h");
36832 var size = config.initialSize || config.width;
36833 if(typeof size != "undefined"){
36834 this.el.setWidth(size);
36837 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36838 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36839 getBox : function(){
36840 if(this.collapsed){
36841 return this.collapsedEl.getBox();
36843 var box = this.el.getBox();
36845 var sw = this.split.el.getWidth();
36852 updateBox : function(box){
36853 if(this.split && !this.collapsed){
36854 var sw = this.split.el.getWidth();
36856 this.split.el.setLeft(box.x);
36857 this.split.el.setTop(box.y);
36858 this.split.el.setHeight(box.height);
36861 if(this.collapsed){
36862 this.updateBody(null, box.height);
36864 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36868 Roo.bootstrap.layout.West = function(config){
36869 config.region = "west";
36870 config.cursor = "w-resize";
36872 Roo.bootstrap.layout.Split.call(this, config);
36874 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36875 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36876 this.split.el.addClass("roo-layout-split-h");
36880 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36881 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36883 onRender: function(ctr, pos)
36885 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36886 var size = this.config.initialSize || this.config.width;
36887 if(typeof size != "undefined"){
36888 this.el.setWidth(size);
36892 getBox : function(){
36893 if(this.collapsed){
36894 return this.collapsedEl.getBox();
36896 var box = this.el.getBox();
36898 box.width += this.split.el.getWidth();
36903 updateBox : function(box){
36904 if(this.split && !this.collapsed){
36905 var sw = this.split.el.getWidth();
36907 this.split.el.setLeft(box.x+box.width);
36908 this.split.el.setTop(box.y);
36909 this.split.el.setHeight(box.height);
36911 if(this.collapsed){
36912 this.updateBody(null, box.height);
36914 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36917 Roo.namespace("Roo.bootstrap.panel");/*
36919 * Ext JS Library 1.1.1
36920 * Copyright(c) 2006-2007, Ext JS, LLC.
36922 * Originally Released Under LGPL - original licence link has changed is not relivant.
36925 * <script type="text/javascript">
36928 * @class Roo.ContentPanel
36929 * @extends Roo.util.Observable
36930 * A basic ContentPanel element.
36931 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36932 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36933 * @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
36934 * @cfg {Boolean} closable True if the panel can be closed/removed
36935 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36936 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36937 * @cfg {Toolbar} toolbar A toolbar for this panel
36938 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36939 * @cfg {String} title The title for this panel
36940 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36941 * @cfg {String} url Calls {@link #setUrl} with this value
36942 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36943 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36944 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36945 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36946 * @cfg {Boolean} badges render the badges
36949 * Create a new ContentPanel.
36950 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36951 * @param {String/Object} config A string to set only the title or a config object
36952 * @param {String} content (optional) Set the HTML content for this panel
36953 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36955 Roo.bootstrap.panel.Content = function( config){
36957 this.tpl = config.tpl || false;
36959 var el = config.el;
36960 var content = config.content;
36962 if(config.autoCreate){ // xtype is available if this is called from factory
36965 this.el = Roo.get(el);
36966 if(!this.el && config && config.autoCreate){
36967 if(typeof config.autoCreate == "object"){
36968 if(!config.autoCreate.id){
36969 config.autoCreate.id = config.id||el;
36971 this.el = Roo.DomHelper.append(document.body,
36972 config.autoCreate, true);
36974 var elcfg = { tag: "div",
36975 cls: "roo-layout-inactive-content",
36979 elcfg.html = config.html;
36983 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36986 this.closable = false;
36987 this.loaded = false;
36988 this.active = false;
36991 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36993 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36995 this.wrapEl = this.el; //this.el.wrap();
36997 if (config.toolbar.items) {
36998 ti = config.toolbar.items ;
36999 delete config.toolbar.items ;
37003 this.toolbar.render(this.wrapEl, 'before');
37004 for(var i =0;i < ti.length;i++) {
37005 // Roo.log(['add child', items[i]]);
37006 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37008 this.toolbar.items = nitems;
37009 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37010 delete config.toolbar;
37014 // xtype created footer. - not sure if will work as we normally have to render first..
37015 if (this.footer && !this.footer.el && this.footer.xtype) {
37016 if (!this.wrapEl) {
37017 this.wrapEl = this.el.wrap();
37020 this.footer.container = this.wrapEl.createChild();
37022 this.footer = Roo.factory(this.footer, Roo);
37027 if(typeof config == "string"){
37028 this.title = config;
37030 Roo.apply(this, config);
37034 this.resizeEl = Roo.get(this.resizeEl, true);
37036 this.resizeEl = this.el;
37038 // handle view.xtype
37046 * Fires when this panel is activated.
37047 * @param {Roo.ContentPanel} this
37051 * @event deactivate
37052 * Fires when this panel is activated.
37053 * @param {Roo.ContentPanel} this
37055 "deactivate" : true,
37059 * Fires when this panel is resized if fitToFrame is true.
37060 * @param {Roo.ContentPanel} this
37061 * @param {Number} width The width after any component adjustments
37062 * @param {Number} height The height after any component adjustments
37068 * Fires when this tab is created
37069 * @param {Roo.ContentPanel} this
37080 if(this.autoScroll){
37081 this.resizeEl.setStyle("overflow", "auto");
37083 // fix randome scrolling
37084 //this.el.on('scroll', function() {
37085 // Roo.log('fix random scolling');
37086 // this.scrollTo('top',0);
37089 content = content || this.content;
37091 this.setContent(content);
37093 if(config && config.url){
37094 this.setUrl(this.url, this.params, this.loadOnce);
37099 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37101 if (this.view && typeof(this.view.xtype) != 'undefined') {
37102 this.view.el = this.el.appendChild(document.createElement("div"));
37103 this.view = Roo.factory(this.view);
37104 this.view.render && this.view.render(false, '');
37108 this.fireEvent('render', this);
37111 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37115 setRegion : function(region){
37116 this.region = region;
37117 this.setActiveClass(region && !this.background);
37121 setActiveClass: function(state)
37124 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37125 this.el.setStyle('position','relative');
37127 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37128 this.el.setStyle('position', 'absolute');
37133 * Returns the toolbar for this Panel if one was configured.
37134 * @return {Roo.Toolbar}
37136 getToolbar : function(){
37137 return this.toolbar;
37140 setActiveState : function(active)
37142 this.active = active;
37143 this.setActiveClass(active);
37145 if(this.fireEvent("deactivate", this) === false){
37150 this.fireEvent("activate", this);
37154 * Updates this panel's element
37155 * @param {String} content The new content
37156 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37158 setContent : function(content, loadScripts){
37159 this.el.update(content, loadScripts);
37162 ignoreResize : function(w, h){
37163 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37166 this.lastSize = {width: w, height: h};
37171 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37172 * @return {Roo.UpdateManager} The UpdateManager
37174 getUpdateManager : function(){
37175 return this.el.getUpdateManager();
37178 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37179 * @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:
37182 url: "your-url.php",
37183 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37184 callback: yourFunction,
37185 scope: yourObject, //(optional scope)
37188 text: "Loading...",
37193 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37194 * 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.
37195 * @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}
37196 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37197 * @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.
37198 * @return {Roo.ContentPanel} this
37201 var um = this.el.getUpdateManager();
37202 um.update.apply(um, arguments);
37208 * 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.
37209 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37210 * @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)
37211 * @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)
37212 * @return {Roo.UpdateManager} The UpdateManager
37214 setUrl : function(url, params, loadOnce){
37215 if(this.refreshDelegate){
37216 this.removeListener("activate", this.refreshDelegate);
37218 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37219 this.on("activate", this.refreshDelegate);
37220 return this.el.getUpdateManager();
37223 _handleRefresh : function(url, params, loadOnce){
37224 if(!loadOnce || !this.loaded){
37225 var updater = this.el.getUpdateManager();
37226 updater.update(url, params, this._setLoaded.createDelegate(this));
37230 _setLoaded : function(){
37231 this.loaded = true;
37235 * Returns this panel's id
37238 getId : function(){
37243 * Returns this panel's element - used by regiosn to add.
37244 * @return {Roo.Element}
37246 getEl : function(){
37247 return this.wrapEl || this.el;
37252 adjustForComponents : function(width, height)
37254 //Roo.log('adjustForComponents ');
37255 if(this.resizeEl != this.el){
37256 width -= this.el.getFrameWidth('lr');
37257 height -= this.el.getFrameWidth('tb');
37260 var te = this.toolbar.getEl();
37261 te.setWidth(width);
37262 height -= te.getHeight();
37265 var te = this.footer.getEl();
37266 te.setWidth(width);
37267 height -= te.getHeight();
37271 if(this.adjustments){
37272 width += this.adjustments[0];
37273 height += this.adjustments[1];
37275 return {"width": width, "height": height};
37278 setSize : function(width, height){
37279 if(this.fitToFrame && !this.ignoreResize(width, height)){
37280 if(this.fitContainer && this.resizeEl != this.el){
37281 this.el.setSize(width, height);
37283 var size = this.adjustForComponents(width, height);
37284 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37285 this.fireEvent('resize', this, size.width, size.height);
37290 * Returns this panel's title
37293 getTitle : function(){
37295 if (typeof(this.title) != 'object') {
37300 for (var k in this.title) {
37301 if (!this.title.hasOwnProperty(k)) {
37305 if (k.indexOf('-') >= 0) {
37306 var s = k.split('-');
37307 for (var i = 0; i<s.length; i++) {
37308 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37311 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37318 * Set this panel's title
37319 * @param {String} title
37321 setTitle : function(title){
37322 this.title = title;
37324 this.region.updatePanelTitle(this, title);
37329 * Returns true is this panel was configured to be closable
37330 * @return {Boolean}
37332 isClosable : function(){
37333 return this.closable;
37336 beforeSlide : function(){
37338 this.resizeEl.clip();
37341 afterSlide : function(){
37343 this.resizeEl.unclip();
37347 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37348 * Will fail silently if the {@link #setUrl} method has not been called.
37349 * This does not activate the panel, just updates its content.
37351 refresh : function(){
37352 if(this.refreshDelegate){
37353 this.loaded = false;
37354 this.refreshDelegate();
37359 * Destroys this panel
37361 destroy : function(){
37362 this.el.removeAllListeners();
37363 var tempEl = document.createElement("span");
37364 tempEl.appendChild(this.el.dom);
37365 tempEl.innerHTML = "";
37371 * form - if the content panel contains a form - this is a reference to it.
37372 * @type {Roo.form.Form}
37376 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37377 * This contains a reference to it.
37383 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37393 * @param {Object} cfg Xtype definition of item to add.
37397 getChildContainer: function () {
37398 return this.getEl();
37403 var ret = new Roo.factory(cfg);
37408 if (cfg.xtype.match(/^Form$/)) {
37411 //if (this.footer) {
37412 // el = this.footer.container.insertSibling(false, 'before');
37414 el = this.el.createChild();
37417 this.form = new Roo.form.Form(cfg);
37420 if ( this.form.allItems.length) {
37421 this.form.render(el.dom);
37425 // should only have one of theses..
37426 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37427 // views.. should not be just added - used named prop 'view''
37429 cfg.el = this.el.appendChild(document.createElement("div"));
37432 var ret = new Roo.factory(cfg);
37434 ret.render && ret.render(false, ''); // render blank..
37444 * @class Roo.bootstrap.panel.Grid
37445 * @extends Roo.bootstrap.panel.Content
37447 * Create a new GridPanel.
37448 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37449 * @param {Object} config A the config object
37455 Roo.bootstrap.panel.Grid = function(config)
37459 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37460 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37462 config.el = this.wrapper;
37463 //this.el = this.wrapper;
37465 if (config.container) {
37466 // ctor'ed from a Border/panel.grid
37469 this.wrapper.setStyle("overflow", "hidden");
37470 this.wrapper.addClass('roo-grid-container');
37475 if(config.toolbar){
37476 var tool_el = this.wrapper.createChild();
37477 this.toolbar = Roo.factory(config.toolbar);
37479 if (config.toolbar.items) {
37480 ti = config.toolbar.items ;
37481 delete config.toolbar.items ;
37485 this.toolbar.render(tool_el);
37486 for(var i =0;i < ti.length;i++) {
37487 // Roo.log(['add child', items[i]]);
37488 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37490 this.toolbar.items = nitems;
37492 delete config.toolbar;
37495 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37496 config.grid.scrollBody = true;;
37497 config.grid.monitorWindowResize = false; // turn off autosizing
37498 config.grid.autoHeight = false;
37499 config.grid.autoWidth = false;
37501 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37503 if (config.background) {
37504 // render grid on panel activation (if panel background)
37505 this.on('activate', function(gp) {
37506 if (!gp.grid.rendered) {
37507 gp.grid.render(this.wrapper);
37508 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37513 this.grid.render(this.wrapper);
37514 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37517 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37518 // ??? needed ??? config.el = this.wrapper;
37523 // xtype created footer. - not sure if will work as we normally have to render first..
37524 if (this.footer && !this.footer.el && this.footer.xtype) {
37526 var ctr = this.grid.getView().getFooterPanel(true);
37527 this.footer.dataSource = this.grid.dataSource;
37528 this.footer = Roo.factory(this.footer, Roo);
37529 this.footer.render(ctr);
37539 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37540 getId : function(){
37541 return this.grid.id;
37545 * Returns the grid for this panel
37546 * @return {Roo.bootstrap.Table}
37548 getGrid : function(){
37552 setSize : function(width, height){
37553 if(!this.ignoreResize(width, height)){
37554 var grid = this.grid;
37555 var size = this.adjustForComponents(width, height);
37556 var gridel = grid.getGridEl();
37557 gridel.setSize(size.width, size.height);
37559 var thd = grid.getGridEl().select('thead',true).first();
37560 var tbd = grid.getGridEl().select('tbody', true).first();
37562 tbd.setSize(width, height - thd.getHeight());
37571 beforeSlide : function(){
37572 this.grid.getView().scroller.clip();
37575 afterSlide : function(){
37576 this.grid.getView().scroller.unclip();
37579 destroy : function(){
37580 this.grid.destroy();
37582 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37587 * @class Roo.bootstrap.panel.Nest
37588 * @extends Roo.bootstrap.panel.Content
37590 * Create a new Panel, that can contain a layout.Border.
37593 * @param {Roo.BorderLayout} layout The layout for this panel
37594 * @param {String/Object} config A string to set only the title or a config object
37596 Roo.bootstrap.panel.Nest = function(config)
37598 // construct with only one argument..
37599 /* FIXME - implement nicer consturctors
37600 if (layout.layout) {
37602 layout = config.layout;
37603 delete config.layout;
37605 if (layout.xtype && !layout.getEl) {
37606 // then layout needs constructing..
37607 layout = Roo.factory(layout, Roo);
37611 config.el = config.layout.getEl();
37613 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37615 config.layout.monitorWindowResize = false; // turn off autosizing
37616 this.layout = config.layout;
37617 this.layout.getEl().addClass("roo-layout-nested-layout");
37624 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37626 setSize : function(width, height){
37627 if(!this.ignoreResize(width, height)){
37628 var size = this.adjustForComponents(width, height);
37629 var el = this.layout.getEl();
37630 if (size.height < 1) {
37631 el.setWidth(size.width);
37633 el.setSize(size.width, size.height);
37635 var touch = el.dom.offsetWidth;
37636 this.layout.layout();
37637 // ie requires a double layout on the first pass
37638 if(Roo.isIE && !this.initialized){
37639 this.initialized = true;
37640 this.layout.layout();
37645 // activate all subpanels if not currently active..
37647 setActiveState : function(active){
37648 this.active = active;
37649 this.setActiveClass(active);
37652 this.fireEvent("deactivate", this);
37656 this.fireEvent("activate", this);
37657 // not sure if this should happen before or after..
37658 if (!this.layout) {
37659 return; // should not happen..
37662 for (var r in this.layout.regions) {
37663 reg = this.layout.getRegion(r);
37664 if (reg.getActivePanel()) {
37665 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37666 reg.setActivePanel(reg.getActivePanel());
37669 if (!reg.panels.length) {
37672 reg.showPanel(reg.getPanel(0));
37681 * Returns the nested BorderLayout for this panel
37682 * @return {Roo.BorderLayout}
37684 getLayout : function(){
37685 return this.layout;
37689 * Adds a xtype elements to the layout of the nested panel
37693 xtype : 'ContentPanel',
37700 xtype : 'NestedLayoutPanel',
37706 items : [ ... list of content panels or nested layout panels.. ]
37710 * @param {Object} cfg Xtype definition of item to add.
37712 addxtype : function(cfg) {
37713 return this.layout.addxtype(cfg);
37718 * Ext JS Library 1.1.1
37719 * Copyright(c) 2006-2007, Ext JS, LLC.
37721 * Originally Released Under LGPL - original licence link has changed is not relivant.
37724 * <script type="text/javascript">
37727 * @class Roo.TabPanel
37728 * @extends Roo.util.Observable
37729 * A lightweight tab container.
37733 // basic tabs 1, built from existing content
37734 var tabs = new Roo.TabPanel("tabs1");
37735 tabs.addTab("script", "View Script");
37736 tabs.addTab("markup", "View Markup");
37737 tabs.activate("script");
37739 // more advanced tabs, built from javascript
37740 var jtabs = new Roo.TabPanel("jtabs");
37741 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37743 // set up the UpdateManager
37744 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37745 var updater = tab2.getUpdateManager();
37746 updater.setDefaultUrl("ajax1.htm");
37747 tab2.on('activate', updater.refresh, updater, true);
37749 // Use setUrl for Ajax loading
37750 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37751 tab3.setUrl("ajax2.htm", null, true);
37754 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37757 jtabs.activate("jtabs-1");
37760 * Create a new TabPanel.
37761 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37762 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37764 Roo.bootstrap.panel.Tabs = function(config){
37766 * The container element for this TabPanel.
37767 * @type Roo.Element
37769 this.el = Roo.get(config.el);
37772 if(typeof config == "boolean"){
37773 this.tabPosition = config ? "bottom" : "top";
37775 Roo.apply(this, config);
37779 if(this.tabPosition == "bottom"){
37780 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37781 this.el.addClass("roo-tabs-bottom");
37783 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37784 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37785 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37787 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37789 if(this.tabPosition != "bottom"){
37790 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37791 * @type Roo.Element
37793 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37794 this.el.addClass("roo-tabs-top");
37798 this.bodyEl.setStyle("position", "relative");
37800 this.active = null;
37801 this.activateDelegate = this.activate.createDelegate(this);
37806 * Fires when the active tab changes
37807 * @param {Roo.TabPanel} this
37808 * @param {Roo.TabPanelItem} activePanel The new active tab
37812 * @event beforetabchange
37813 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37814 * @param {Roo.TabPanel} this
37815 * @param {Object} e Set cancel to true on this object to cancel the tab change
37816 * @param {Roo.TabPanelItem} tab The tab being changed to
37818 "beforetabchange" : true
37821 Roo.EventManager.onWindowResize(this.onResize, this);
37822 this.cpad = this.el.getPadding("lr");
37823 this.hiddenCount = 0;
37826 // toolbar on the tabbar support...
37827 if (this.toolbar) {
37828 alert("no toolbar support yet");
37829 this.toolbar = false;
37831 var tcfg = this.toolbar;
37832 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37833 this.toolbar = new Roo.Toolbar(tcfg);
37834 if (Roo.isSafari) {
37835 var tbl = tcfg.container.child('table', true);
37836 tbl.setAttribute('width', '100%');
37844 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37847 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37849 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37851 tabPosition : "top",
37853 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37855 currentTabWidth : 0,
37857 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37861 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37865 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37867 preferredTabWidth : 175,
37869 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37871 resizeTabs : false,
37873 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37875 monitorResize : true,
37877 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37882 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37883 * @param {String} id The id of the div to use <b>or create</b>
37884 * @param {String} text The text for the tab
37885 * @param {String} content (optional) Content to put in the TabPanelItem body
37886 * @param {Boolean} closable (optional) True to create a close icon on the tab
37887 * @return {Roo.TabPanelItem} The created TabPanelItem
37889 addTab : function(id, text, content, closable, tpl)
37891 var item = new Roo.bootstrap.panel.TabItem({
37895 closable : closable,
37898 this.addTabItem(item);
37900 item.setContent(content);
37906 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37907 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37908 * @return {Roo.TabPanelItem}
37910 getTab : function(id){
37911 return this.items[id];
37915 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37916 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37918 hideTab : function(id){
37919 var t = this.items[id];
37922 this.hiddenCount++;
37923 this.autoSizeTabs();
37928 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37929 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37931 unhideTab : function(id){
37932 var t = this.items[id];
37934 t.setHidden(false);
37935 this.hiddenCount--;
37936 this.autoSizeTabs();
37941 * Adds an existing {@link Roo.TabPanelItem}.
37942 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37944 addTabItem : function(item){
37945 this.items[item.id] = item;
37946 this.items.push(item);
37947 // if(this.resizeTabs){
37948 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37949 // this.autoSizeTabs();
37951 // item.autoSize();
37956 * Removes a {@link Roo.TabPanelItem}.
37957 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37959 removeTab : function(id){
37960 var items = this.items;
37961 var tab = items[id];
37962 if(!tab) { return; }
37963 var index = items.indexOf(tab);
37964 if(this.active == tab && items.length > 1){
37965 var newTab = this.getNextAvailable(index);
37970 this.stripEl.dom.removeChild(tab.pnode.dom);
37971 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37972 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37974 items.splice(index, 1);
37975 delete this.items[tab.id];
37976 tab.fireEvent("close", tab);
37977 tab.purgeListeners();
37978 this.autoSizeTabs();
37981 getNextAvailable : function(start){
37982 var items = this.items;
37984 // look for a next tab that will slide over to
37985 // replace the one being removed
37986 while(index < items.length){
37987 var item = items[++index];
37988 if(item && !item.isHidden()){
37992 // if one isn't found select the previous tab (on the left)
37995 var item = items[--index];
37996 if(item && !item.isHidden()){
38004 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38005 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38007 disableTab : function(id){
38008 var tab = this.items[id];
38009 if(tab && this.active != tab){
38015 * Enables a {@link Roo.TabPanelItem} that is disabled.
38016 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38018 enableTab : function(id){
38019 var tab = this.items[id];
38024 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38025 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38026 * @return {Roo.TabPanelItem} The TabPanelItem.
38028 activate : function(id){
38029 var tab = this.items[id];
38033 if(tab == this.active || tab.disabled){
38037 this.fireEvent("beforetabchange", this, e, tab);
38038 if(e.cancel !== true && !tab.disabled){
38040 this.active.hide();
38042 this.active = this.items[id];
38043 this.active.show();
38044 this.fireEvent("tabchange", this, this.active);
38050 * Gets the active {@link Roo.TabPanelItem}.
38051 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38053 getActiveTab : function(){
38054 return this.active;
38058 * Updates the tab body element to fit the height of the container element
38059 * for overflow scrolling
38060 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38062 syncHeight : function(targetHeight){
38063 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38064 var bm = this.bodyEl.getMargins();
38065 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38066 this.bodyEl.setHeight(newHeight);
38070 onResize : function(){
38071 if(this.monitorResize){
38072 this.autoSizeTabs();
38077 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38079 beginUpdate : function(){
38080 this.updating = true;
38084 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38086 endUpdate : function(){
38087 this.updating = false;
38088 this.autoSizeTabs();
38092 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38094 autoSizeTabs : function(){
38095 var count = this.items.length;
38096 var vcount = count - this.hiddenCount;
38097 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38100 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38101 var availWidth = Math.floor(w / vcount);
38102 var b = this.stripBody;
38103 if(b.getWidth() > w){
38104 var tabs = this.items;
38105 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38106 if(availWidth < this.minTabWidth){
38107 /*if(!this.sleft){ // incomplete scrolling code
38108 this.createScrollButtons();
38111 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38114 if(this.currentTabWidth < this.preferredTabWidth){
38115 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38121 * Returns the number of tabs in this TabPanel.
38124 getCount : function(){
38125 return this.items.length;
38129 * Resizes all the tabs to the passed width
38130 * @param {Number} The new width
38132 setTabWidth : function(width){
38133 this.currentTabWidth = width;
38134 for(var i = 0, len = this.items.length; i < len; i++) {
38135 if(!this.items[i].isHidden()) {
38136 this.items[i].setWidth(width);
38142 * Destroys this TabPanel
38143 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38145 destroy : function(removeEl){
38146 Roo.EventManager.removeResizeListener(this.onResize, this);
38147 for(var i = 0, len = this.items.length; i < len; i++){
38148 this.items[i].purgeListeners();
38150 if(removeEl === true){
38151 this.el.update("");
38156 createStrip : function(container)
38158 var strip = document.createElement("nav");
38159 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38160 container.appendChild(strip);
38164 createStripList : function(strip)
38166 // div wrapper for retard IE
38167 // returns the "tr" element.
38168 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38169 //'<div class="x-tabs-strip-wrap">'+
38170 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38171 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38172 return strip.firstChild; //.firstChild.firstChild.firstChild;
38174 createBody : function(container)
38176 var body = document.createElement("div");
38177 Roo.id(body, "tab-body");
38178 //Roo.fly(body).addClass("x-tabs-body");
38179 Roo.fly(body).addClass("tab-content");
38180 container.appendChild(body);
38183 createItemBody :function(bodyEl, id){
38184 var body = Roo.getDom(id);
38186 body = document.createElement("div");
38189 //Roo.fly(body).addClass("x-tabs-item-body");
38190 Roo.fly(body).addClass("tab-pane");
38191 bodyEl.insertBefore(body, bodyEl.firstChild);
38195 createStripElements : function(stripEl, text, closable, tpl)
38197 var td = document.createElement("li"); // was td..
38200 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38203 stripEl.appendChild(td);
38205 td.className = "x-tabs-closable";
38206 if(!this.closeTpl){
38207 this.closeTpl = new Roo.Template(
38208 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38209 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38210 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38213 var el = this.closeTpl.overwrite(td, {"text": text});
38214 var close = el.getElementsByTagName("div")[0];
38215 var inner = el.getElementsByTagName("em")[0];
38216 return {"el": el, "close": close, "inner": inner};
38219 // not sure what this is..
38220 // if(!this.tabTpl){
38221 //this.tabTpl = new Roo.Template(
38222 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38223 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38225 // this.tabTpl = new Roo.Template(
38226 // '<a href="#">' +
38227 // '<span unselectable="on"' +
38228 // (this.disableTooltips ? '' : ' title="{text}"') +
38229 // ' >{text}</span></a>'
38235 var template = tpl || this.tabTpl || false;
38239 template = new Roo.Template(
38241 '<span unselectable="on"' +
38242 (this.disableTooltips ? '' : ' title="{text}"') +
38243 ' >{text}</span></a>'
38247 switch (typeof(template)) {
38251 template = new Roo.Template(template);
38257 var el = template.overwrite(td, {"text": text});
38259 var inner = el.getElementsByTagName("span")[0];
38261 return {"el": el, "inner": inner};
38269 * @class Roo.TabPanelItem
38270 * @extends Roo.util.Observable
38271 * Represents an individual item (tab plus body) in a TabPanel.
38272 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38273 * @param {String} id The id of this TabPanelItem
38274 * @param {String} text The text for the tab of this TabPanelItem
38275 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38277 Roo.bootstrap.panel.TabItem = function(config){
38279 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38280 * @type Roo.TabPanel
38282 this.tabPanel = config.panel;
38284 * The id for this TabPanelItem
38287 this.id = config.id;
38289 this.disabled = false;
38291 this.text = config.text;
38293 this.loaded = false;
38294 this.closable = config.closable;
38297 * The body element for this TabPanelItem.
38298 * @type Roo.Element
38300 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38301 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38302 this.bodyEl.setStyle("display", "block");
38303 this.bodyEl.setStyle("zoom", "1");
38304 //this.hideAction();
38306 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38308 this.el = Roo.get(els.el);
38309 this.inner = Roo.get(els.inner, true);
38310 this.textEl = Roo.get(this.el.dom.firstChild, true);
38311 this.pnode = Roo.get(els.el.parentNode, true);
38312 // this.el.on("mousedown", this.onTabMouseDown, this);
38313 this.el.on("click", this.onTabClick, this);
38315 if(config.closable){
38316 var c = Roo.get(els.close, true);
38317 c.dom.title = this.closeText;
38318 c.addClassOnOver("close-over");
38319 c.on("click", this.closeClick, this);
38325 * Fires when this tab becomes the active tab.
38326 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38327 * @param {Roo.TabPanelItem} this
38331 * @event beforeclose
38332 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38333 * @param {Roo.TabPanelItem} this
38334 * @param {Object} e Set cancel to true on this object to cancel the close.
38336 "beforeclose": true,
38339 * Fires when this tab is closed.
38340 * @param {Roo.TabPanelItem} this
38344 * @event deactivate
38345 * Fires when this tab is no longer the active tab.
38346 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38347 * @param {Roo.TabPanelItem} this
38349 "deactivate" : true
38351 this.hidden = false;
38353 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38356 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38358 purgeListeners : function(){
38359 Roo.util.Observable.prototype.purgeListeners.call(this);
38360 this.el.removeAllListeners();
38363 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38366 this.pnode.addClass("active");
38369 this.tabPanel.stripWrap.repaint();
38371 this.fireEvent("activate", this.tabPanel, this);
38375 * Returns true if this tab is the active tab.
38376 * @return {Boolean}
38378 isActive : function(){
38379 return this.tabPanel.getActiveTab() == this;
38383 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38386 this.pnode.removeClass("active");
38388 this.fireEvent("deactivate", this.tabPanel, this);
38391 hideAction : function(){
38392 this.bodyEl.hide();
38393 this.bodyEl.setStyle("position", "absolute");
38394 this.bodyEl.setLeft("-20000px");
38395 this.bodyEl.setTop("-20000px");
38398 showAction : function(){
38399 this.bodyEl.setStyle("position", "relative");
38400 this.bodyEl.setTop("");
38401 this.bodyEl.setLeft("");
38402 this.bodyEl.show();
38406 * Set the tooltip for the tab.
38407 * @param {String} tooltip The tab's tooltip
38409 setTooltip : function(text){
38410 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38411 this.textEl.dom.qtip = text;
38412 this.textEl.dom.removeAttribute('title');
38414 this.textEl.dom.title = text;
38418 onTabClick : function(e){
38419 e.preventDefault();
38420 this.tabPanel.activate(this.id);
38423 onTabMouseDown : function(e){
38424 e.preventDefault();
38425 this.tabPanel.activate(this.id);
38428 getWidth : function(){
38429 return this.inner.getWidth();
38432 setWidth : function(width){
38433 var iwidth = width - this.pnode.getPadding("lr");
38434 this.inner.setWidth(iwidth);
38435 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38436 this.pnode.setWidth(width);
38440 * Show or hide the tab
38441 * @param {Boolean} hidden True to hide or false to show.
38443 setHidden : function(hidden){
38444 this.hidden = hidden;
38445 this.pnode.setStyle("display", hidden ? "none" : "");
38449 * Returns true if this tab is "hidden"
38450 * @return {Boolean}
38452 isHidden : function(){
38453 return this.hidden;
38457 * Returns the text for this tab
38460 getText : function(){
38464 autoSize : function(){
38465 //this.el.beginMeasure();
38466 this.textEl.setWidth(1);
38468 * #2804 [new] Tabs in Roojs
38469 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38471 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38472 //this.el.endMeasure();
38476 * Sets the text for the tab (Note: this also sets the tooltip text)
38477 * @param {String} text The tab's text and tooltip
38479 setText : function(text){
38481 this.textEl.update(text);
38482 this.setTooltip(text);
38483 //if(!this.tabPanel.resizeTabs){
38484 // this.autoSize();
38488 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38490 activate : function(){
38491 this.tabPanel.activate(this.id);
38495 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38497 disable : function(){
38498 if(this.tabPanel.active != this){
38499 this.disabled = true;
38500 this.pnode.addClass("disabled");
38505 * Enables this TabPanelItem if it was previously disabled.
38507 enable : function(){
38508 this.disabled = false;
38509 this.pnode.removeClass("disabled");
38513 * Sets the content for this TabPanelItem.
38514 * @param {String} content The content
38515 * @param {Boolean} loadScripts true to look for and load scripts
38517 setContent : function(content, loadScripts){
38518 this.bodyEl.update(content, loadScripts);
38522 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38523 * @return {Roo.UpdateManager} The UpdateManager
38525 getUpdateManager : function(){
38526 return this.bodyEl.getUpdateManager();
38530 * Set a URL to be used to load the content for this TabPanelItem.
38531 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38532 * @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)
38533 * @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)
38534 * @return {Roo.UpdateManager} The UpdateManager
38536 setUrl : function(url, params, loadOnce){
38537 if(this.refreshDelegate){
38538 this.un('activate', this.refreshDelegate);
38540 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38541 this.on("activate", this.refreshDelegate);
38542 return this.bodyEl.getUpdateManager();
38546 _handleRefresh : function(url, params, loadOnce){
38547 if(!loadOnce || !this.loaded){
38548 var updater = this.bodyEl.getUpdateManager();
38549 updater.update(url, params, this._setLoaded.createDelegate(this));
38554 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38555 * Will fail silently if the setUrl method has not been called.
38556 * This does not activate the panel, just updates its content.
38558 refresh : function(){
38559 if(this.refreshDelegate){
38560 this.loaded = false;
38561 this.refreshDelegate();
38566 _setLoaded : function(){
38567 this.loaded = true;
38571 closeClick : function(e){
38574 this.fireEvent("beforeclose", this, o);
38575 if(o.cancel !== true){
38576 this.tabPanel.removeTab(this.id);
38580 * The text displayed in the tooltip for the close icon.
38583 closeText : "Close this tab"
38586 * This script refer to:
38587 * Title: International Telephone Input
38588 * Author: Jack O'Connor
38589 * Code version: v12.1.12
38590 * Availability: https://github.com/jackocnr/intl-tel-input.git
38593 Roo.bootstrap.PhoneInputData = function() {
38596 "Afghanistan (افغانستان)",
38601 "Albania (Shqipëri)",
38606 "Algeria (الجزائر)",
38631 "Antigua and Barbuda",
38641 "Armenia (Հայաստան)",
38657 "Austria (Österreich)",
38662 "Azerbaijan (Azərbaycan)",
38672 "Bahrain (البحرين)",
38677 "Bangladesh (বাংলাদেশ)",
38687 "Belarus (Беларусь)",
38692 "Belgium (België)",
38722 "Bosnia and Herzegovina (Босна и Херцеговина)",
38737 "British Indian Ocean Territory",
38742 "British Virgin Islands",
38752 "Bulgaria (България)",
38762 "Burundi (Uburundi)",
38767 "Cambodia (កម្ពុជា)",
38772 "Cameroon (Cameroun)",
38781 ["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"]
38784 "Cape Verde (Kabu Verdi)",
38789 "Caribbean Netherlands",
38800 "Central African Republic (République centrafricaine)",
38820 "Christmas Island",
38826 "Cocos (Keeling) Islands",
38837 "Comoros (جزر القمر)",
38842 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38847 "Congo (Republic) (Congo-Brazzaville)",
38867 "Croatia (Hrvatska)",
38888 "Czech Republic (Česká republika)",
38893 "Denmark (Danmark)",
38908 "Dominican Republic (República Dominicana)",
38912 ["809", "829", "849"]
38930 "Equatorial Guinea (Guinea Ecuatorial)",
38950 "Falkland Islands (Islas Malvinas)",
38955 "Faroe Islands (Føroyar)",
38976 "French Guiana (Guyane française)",
38981 "French Polynesia (Polynésie française)",
38996 "Georgia (საქართველო)",
39001 "Germany (Deutschland)",
39021 "Greenland (Kalaallit Nunaat)",
39058 "Guinea-Bissau (Guiné Bissau)",
39083 "Hungary (Magyarország)",
39088 "Iceland (Ísland)",
39108 "Iraq (العراق)",
39124 "Israel (ישראל)",
39151 "Jordan (الأردن)",
39156 "Kazakhstan (Казахстан)",
39177 "Kuwait (الكويت)",
39182 "Kyrgyzstan (Кыргызстан)",
39192 "Latvia (Latvija)",
39197 "Lebanon (لبنان)",
39212 "Libya (ليبيا)",
39222 "Lithuania (Lietuva)",
39237 "Macedonia (FYROM) (Македонија)",
39242 "Madagascar (Madagasikara)",
39272 "Marshall Islands",
39282 "Mauritania (موريتانيا)",
39287 "Mauritius (Moris)",
39308 "Moldova (Republica Moldova)",
39318 "Mongolia (Монгол)",
39323 "Montenegro (Crna Gora)",
39333 "Morocco (المغرب)",
39339 "Mozambique (Moçambique)",
39344 "Myanmar (Burma) (မြန်မာ)",
39349 "Namibia (Namibië)",
39364 "Netherlands (Nederland)",
39369 "New Caledonia (Nouvelle-Calédonie)",
39404 "North Korea (조선 민주주의 인민 공화국)",
39409 "Northern Mariana Islands",
39425 "Pakistan (پاکستان)",
39435 "Palestine (فلسطين)",
39445 "Papua New Guinea",
39487 "Réunion (La Réunion)",
39493 "Romania (România)",
39509 "Saint Barthélemy",
39520 "Saint Kitts and Nevis",
39530 "Saint Martin (Saint-Martin (partie française))",
39536 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39541 "Saint Vincent and the Grenadines",
39556 "São Tomé and Príncipe (São Tomé e Príncipe)",
39561 "Saudi Arabia (المملكة العربية السعودية)",
39566 "Senegal (Sénégal)",
39596 "Slovakia (Slovensko)",
39601 "Slovenia (Slovenija)",
39611 "Somalia (Soomaaliya)",
39621 "South Korea (대한민국)",
39626 "South Sudan (جنوب السودان)",
39636 "Sri Lanka (ශ්රී ලංකාව)",
39641 "Sudan (السودان)",
39651 "Svalbard and Jan Mayen",
39662 "Sweden (Sverige)",
39667 "Switzerland (Schweiz)",
39672 "Syria (سوريا)",
39717 "Trinidad and Tobago",
39722 "Tunisia (تونس)",
39727 "Turkey (Türkiye)",
39737 "Turks and Caicos Islands",
39747 "U.S. Virgin Islands",
39757 "Ukraine (Україна)",
39762 "United Arab Emirates (الإمارات العربية المتحدة)",
39784 "Uzbekistan (Oʻzbekiston)",
39794 "Vatican City (Città del Vaticano)",
39805 "Vietnam (Việt Nam)",
39810 "Wallis and Futuna (Wallis-et-Futuna)",
39815 "Western Sahara (الصحراء الغربية)",
39821 "Yemen (اليمن)",
39845 * This script refer to:
39846 * Title: International Telephone Input
39847 * Author: Jack O'Connor
39848 * Code version: v12.1.12
39849 * Availability: https://github.com/jackocnr/intl-tel-input.git
39853 * @class Roo.bootstrap.PhoneInput
39854 * @extends Roo.bootstrap.TriggerField
39855 * An input with International dial-code selection
39857 * @cfg {String} defaultDialCode default '+852'
39858 * @cfg {Array} preferedCountries default []
39861 * Create a new PhoneInput.
39862 * @param {Object} config Configuration options
39865 Roo.bootstrap.PhoneInput = function(config) {
39866 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39869 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39871 listWidth: undefined,
39873 selectedClass: 'active',
39875 invalidClass : "has-warning",
39877 validClass: 'has-success',
39879 allowed: '0123456789',
39882 * @cfg {String} defaultDialCode The default dial code when initializing the input
39884 defaultDialCode: '+852',
39887 * @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
39889 preferedCountries: false,
39891 getAutoCreate : function()
39893 var data = Roo.bootstrap.PhoneInputData();
39894 var align = this.labelAlign || this.parentLabelAlign();
39897 this.allCountries = [];
39898 this.dialCodeMapping = [];
39900 for (var i = 0; i < data.length; i++) {
39902 this.allCountries[i] = {
39906 priority: c[3] || 0,
39907 areaCodes: c[4] || null
39909 this.dialCodeMapping[c[2]] = {
39912 priority: c[3] || 0,
39913 areaCodes: c[4] || null
39925 cls : 'form-control tel-input',
39926 autocomplete: 'new-password'
39929 var hiddenInput = {
39932 cls: 'hidden-tel-input'
39936 hiddenInput.name = this.name;
39939 if (this.disabled) {
39940 input.disabled = true;
39943 var flag_container = {
39960 cls: this.hasFeedback ? 'has-feedback' : '',
39966 cls: 'dial-code-holder',
39973 cls: 'roo-select2-container input-group',
39980 if (this.fieldLabel.length) {
39983 tooltip: 'This field is required'
39989 cls: 'control-label',
39995 html: this.fieldLabel
39998 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40004 if(this.indicatorpos == 'right') {
40005 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40012 if(align == 'left') {
40020 if(this.labelWidth > 12){
40021 label.style = "width: " + this.labelWidth + 'px';
40023 if(this.labelWidth < 13 && this.labelmd == 0){
40024 this.labelmd = this.labelWidth;
40026 if(this.labellg > 0){
40027 label.cls += ' col-lg-' + this.labellg;
40028 input.cls += ' col-lg-' + (12 - this.labellg);
40030 if(this.labelmd > 0){
40031 label.cls += ' col-md-' + this.labelmd;
40032 container.cls += ' col-md-' + (12 - this.labelmd);
40034 if(this.labelsm > 0){
40035 label.cls += ' col-sm-' + this.labelsm;
40036 container.cls += ' col-sm-' + (12 - this.labelsm);
40038 if(this.labelxs > 0){
40039 label.cls += ' col-xs-' + this.labelxs;
40040 container.cls += ' col-xs-' + (12 - this.labelxs);
40050 var settings = this;
40052 ['xs','sm','md','lg'].map(function(size){
40053 if (settings[size]) {
40054 cfg.cls += ' col-' + size + '-' + settings[size];
40058 this.store = new Roo.data.Store({
40059 proxy : new Roo.data.MemoryProxy({}),
40060 reader : new Roo.data.JsonReader({
40071 'name' : 'dialCode',
40075 'name' : 'priority',
40079 'name' : 'areaCodes',
40086 if(!this.preferedCountries) {
40087 this.preferedCountries = [
40094 var p = this.preferedCountries.reverse();
40097 for (var i = 0; i < p.length; i++) {
40098 for (var j = 0; j < this.allCountries.length; j++) {
40099 if(this.allCountries[j].iso2 == p[i]) {
40100 var t = this.allCountries[j];
40101 this.allCountries.splice(j,1);
40102 this.allCountries.unshift(t);
40108 this.store.proxy.data = {
40110 data: this.allCountries
40116 initEvents : function()
40119 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40121 this.indicator = this.indicatorEl();
40122 this.flag = this.flagEl();
40123 this.dialCodeHolder = this.dialCodeHolderEl();
40125 this.trigger = this.el.select('div.flag-box',true).first();
40126 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40131 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40132 _this.list.setWidth(lw);
40135 this.list.on('mouseover', this.onViewOver, this);
40136 this.list.on('mousemove', this.onViewMove, this);
40137 this.inputEl().on("keyup", this.onKeyUp, this);
40139 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40141 this.view = new Roo.View(this.list, this.tpl, {
40142 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40145 this.view.on('click', this.onViewClick, this);
40146 this.setValue(this.defaultDialCode);
40149 onTriggerClick : function(e)
40151 Roo.log('trigger click');
40156 if(this.isExpanded()){
40158 this.hasFocus = false;
40160 this.store.load({});
40161 this.hasFocus = true;
40166 isExpanded : function()
40168 return this.list.isVisible();
40171 collapse : function()
40173 if(!this.isExpanded()){
40177 Roo.get(document).un('mousedown', this.collapseIf, this);
40178 Roo.get(document).un('mousewheel', this.collapseIf, this);
40179 this.fireEvent('collapse', this);
40183 expand : function()
40187 if(this.isExpanded() || !this.hasFocus){
40191 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40192 this.list.setWidth(lw);
40195 this.restrictHeight();
40197 Roo.get(document).on('mousedown', this.collapseIf, this);
40198 Roo.get(document).on('mousewheel', this.collapseIf, this);
40200 this.fireEvent('expand', this);
40203 restrictHeight : function()
40205 this.list.alignTo(this.inputEl(), this.listAlign);
40206 this.list.alignTo(this.inputEl(), this.listAlign);
40209 onViewOver : function(e, t)
40211 if(this.inKeyMode){
40214 var item = this.view.findItemFromChild(t);
40217 var index = this.view.indexOf(item);
40218 this.select(index, false);
40223 onViewClick : function(view, doFocus, el, e)
40225 var index = this.view.getSelectedIndexes()[0];
40227 var r = this.store.getAt(index);
40230 this.onSelect(r, index);
40232 if(doFocus !== false && !this.blockFocus){
40233 this.inputEl().focus();
40237 onViewMove : function(e, t)
40239 this.inKeyMode = false;
40242 select : function(index, scrollIntoView)
40244 this.selectedIndex = index;
40245 this.view.select(index);
40246 if(scrollIntoView !== false){
40247 var el = this.view.getNode(index);
40249 this.list.scrollChildIntoView(el, false);
40254 createList : function()
40256 this.list = Roo.get(document.body).createChild({
40258 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40259 style: 'display:none'
40262 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40265 collapseIf : function(e)
40267 var in_combo = e.within(this.el);
40268 var in_list = e.within(this.list);
40269 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40271 if (in_combo || in_list || is_list) {
40277 onSelect : function(record, index)
40279 if(this.fireEvent('beforeselect', this, record, index) !== false){
40281 this.setFlagClass(record.data.iso2);
40282 this.setDialCode(record.data.dialCode);
40283 this.hasFocus = false;
40285 this.fireEvent('select', this, record, index);
40289 flagEl : function()
40291 var flag = this.el.select('div.flag',true).first();
40298 dialCodeHolderEl : function()
40300 var d = this.el.select('input.dial-code-holder',true).first();
40307 setDialCode : function(v)
40309 this.dialCodeHolder.dom.value = '+'+v;
40312 setFlagClass : function(n)
40314 this.flag.dom.className = 'flag '+n;
40317 getValue : function()
40319 var v = this.inputEl().getValue();
40320 if(this.dialCodeHolder) {
40321 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40326 setValue : function(v)
40328 var d = this.getDialCode(v);
40330 //invalid dial code
40331 if(v.length == 0 || !d || d.length == 0) {
40333 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40334 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40340 this.setFlagClass(this.dialCodeMapping[d].iso2);
40341 this.setDialCode(d);
40342 this.inputEl().dom.value = v.replace('+'+d,'');
40343 this.hiddenEl().dom.value = this.getValue();
40348 getDialCode : function(v)
40352 if (v.length == 0) {
40353 return this.dialCodeHolder.dom.value;
40357 if (v.charAt(0) != "+") {
40360 var numericChars = "";
40361 for (var i = 1; i < v.length; i++) {
40362 var c = v.charAt(i);
40365 if (this.dialCodeMapping[numericChars]) {
40366 dialCode = v.substr(1, i);
40368 if (numericChars.length == 4) {
40378 this.setValue(this.defaultDialCode);
40382 hiddenEl : function()
40384 return this.el.select('input.hidden-tel-input',true).first();
40387 onKeyUp : function(e){
40389 var k = e.getKey();
40390 var c = e.getCharCode();
40393 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40394 this.allowed.indexOf(String.fromCharCode(c)) === -1
40399 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40402 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40406 this.setValue(this.getValue());
40411 * @class Roo.bootstrap.MoneyField
40412 * @extends Roo.bootstrap.ComboBox
40413 * Bootstrap MoneyField class
40416 * Create a new MoneyField.
40417 * @param {Object} config Configuration options
40420 Roo.bootstrap.MoneyField = function(config) {
40422 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40426 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40429 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40431 allowDecimals : true,
40433 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40435 decimalSeparator : ".",
40437 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40439 decimalPrecision : 0,
40441 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40443 allowNegative : true,
40445 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40449 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40451 minValue : Number.NEGATIVE_INFINITY,
40453 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40455 maxValue : Number.MAX_VALUE,
40457 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40459 minText : "The minimum value for this field is {0}",
40461 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40463 maxText : "The maximum value for this field is {0}",
40465 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40466 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40468 nanText : "{0} is not a valid number",
40470 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40474 * @cfg {String} defaults currency of the MoneyField
40475 * value should be in lkey
40477 defaultCurrency : false,
40479 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40481 thousandsDelimiter : false,
40491 getAutoCreate : function()
40493 var align = this.labelAlign || this.parentLabelAlign();
40505 cls : 'form-control roo-money-amount-input',
40506 autocomplete: 'new-password'
40509 var hiddenInput = {
40513 cls: 'hidden-number-input'
40517 hiddenInput.name = this.name;
40520 if (this.disabled) {
40521 input.disabled = true;
40524 var clg = 12 - this.inputlg;
40525 var cmd = 12 - this.inputmd;
40526 var csm = 12 - this.inputsm;
40527 var cxs = 12 - this.inputxs;
40531 cls : 'row roo-money-field',
40535 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40539 cls: 'roo-select2-container input-group',
40543 cls : 'form-control roo-money-currency-input',
40544 autocomplete: 'new-password',
40546 name : this.currencyName
40550 cls : 'input-group-addon',
40564 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40568 cls: this.hasFeedback ? 'has-feedback' : '',
40579 if (this.fieldLabel.length) {
40582 tooltip: 'This field is required'
40588 cls: 'control-label',
40594 html: this.fieldLabel
40597 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40603 if(this.indicatorpos == 'right') {
40604 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40611 if(align == 'left') {
40619 if(this.labelWidth > 12){
40620 label.style = "width: " + this.labelWidth + 'px';
40622 if(this.labelWidth < 13 && this.labelmd == 0){
40623 this.labelmd = this.labelWidth;
40625 if(this.labellg > 0){
40626 label.cls += ' col-lg-' + this.labellg;
40627 input.cls += ' col-lg-' + (12 - this.labellg);
40629 if(this.labelmd > 0){
40630 label.cls += ' col-md-' + this.labelmd;
40631 container.cls += ' col-md-' + (12 - this.labelmd);
40633 if(this.labelsm > 0){
40634 label.cls += ' col-sm-' + this.labelsm;
40635 container.cls += ' col-sm-' + (12 - this.labelsm);
40637 if(this.labelxs > 0){
40638 label.cls += ' col-xs-' + this.labelxs;
40639 container.cls += ' col-xs-' + (12 - this.labelxs);
40650 var settings = this;
40652 ['xs','sm','md','lg'].map(function(size){
40653 if (settings[size]) {
40654 cfg.cls += ' col-' + size + '-' + settings[size];
40661 initEvents : function()
40663 this.indicator = this.indicatorEl();
40665 this.initCurrencyEvent();
40667 this.initNumberEvent();
40670 initCurrencyEvent : function()
40673 throw "can not find store for combo";
40676 this.store = Roo.factory(this.store, Roo.data);
40677 this.store.parent = this;
40681 this.triggerEl = this.el.select('.input-group-addon', true).first();
40683 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40688 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40689 _this.list.setWidth(lw);
40692 this.list.on('mouseover', this.onViewOver, this);
40693 this.list.on('mousemove', this.onViewMove, this);
40694 this.list.on('scroll', this.onViewScroll, this);
40697 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40700 this.view = new Roo.View(this.list, this.tpl, {
40701 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40704 this.view.on('click', this.onViewClick, this);
40706 this.store.on('beforeload', this.onBeforeLoad, this);
40707 this.store.on('load', this.onLoad, this);
40708 this.store.on('loadexception', this.onLoadException, this);
40710 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40711 "up" : function(e){
40712 this.inKeyMode = true;
40716 "down" : function(e){
40717 if(!this.isExpanded()){
40718 this.onTriggerClick();
40720 this.inKeyMode = true;
40725 "enter" : function(e){
40728 if(this.fireEvent("specialkey", this, e)){
40729 this.onViewClick(false);
40735 "esc" : function(e){
40739 "tab" : function(e){
40742 if(this.fireEvent("specialkey", this, e)){
40743 this.onViewClick(false);
40751 doRelay : function(foo, bar, hname){
40752 if(hname == 'down' || this.scope.isExpanded()){
40753 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40761 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40765 initNumberEvent : function(e)
40767 this.inputEl().on("keydown" , this.fireKey, this);
40768 this.inputEl().on("focus", this.onFocus, this);
40769 this.inputEl().on("blur", this.onBlur, this);
40771 this.inputEl().relayEvent('keyup', this);
40773 if(this.indicator){
40774 this.indicator.addClass('invisible');
40777 this.originalValue = this.getValue();
40779 if(this.validationEvent == 'keyup'){
40780 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40781 this.inputEl().on('keyup', this.filterValidation, this);
40783 else if(this.validationEvent !== false){
40784 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40787 if(this.selectOnFocus){
40788 this.on("focus", this.preFocus, this);
40791 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40792 this.inputEl().on("keypress", this.filterKeys, this);
40794 this.inputEl().relayEvent('keypress', this);
40797 var allowed = "0123456789";
40799 if(this.allowDecimals){
40800 allowed += this.decimalSeparator;
40803 if(this.allowNegative){
40807 if(this.thousandsDelimiter) {
40811 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40813 var keyPress = function(e){
40815 var k = e.getKey();
40817 var c = e.getCharCode();
40820 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40821 allowed.indexOf(String.fromCharCode(c)) === -1
40827 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40831 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40836 this.inputEl().on("keypress", keyPress, this);
40840 onTriggerClick : function(e)
40847 this.loadNext = false;
40849 if(this.isExpanded()){
40854 this.hasFocus = true;
40856 if(this.triggerAction == 'all') {
40857 this.doQuery(this.allQuery, true);
40861 this.doQuery(this.getRawValue());
40864 getCurrency : function()
40866 var v = this.currencyEl().getValue();
40871 restrictHeight : function()
40873 this.list.alignTo(this.currencyEl(), this.listAlign);
40874 this.list.alignTo(this.currencyEl(), this.listAlign);
40877 onViewClick : function(view, doFocus, el, e)
40879 var index = this.view.getSelectedIndexes()[0];
40881 var r = this.store.getAt(index);
40884 this.onSelect(r, index);
40888 onSelect : function(record, index){
40890 if(this.fireEvent('beforeselect', this, record, index) !== false){
40892 this.setFromCurrencyData(index > -1 ? record.data : false);
40896 this.fireEvent('select', this, record, index);
40900 setFromCurrencyData : function(o)
40904 this.lastCurrency = o;
40906 if (this.currencyField) {
40907 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40909 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40912 this.lastSelectionText = currency;
40914 //setting default currency
40915 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40916 this.setCurrency(this.defaultCurrency);
40920 this.setCurrency(currency);
40923 setFromData : function(o)
40927 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40929 this.setFromCurrencyData(c);
40934 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40936 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40939 this.setValue(value);
40943 setCurrency : function(v)
40945 this.currencyValue = v;
40948 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40953 setValue : function(v)
40955 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40961 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40963 this.inputEl().dom.value = (v == '') ? '' :
40964 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40966 if(!this.allowZero && v === '0') {
40967 this.hiddenEl().dom.value = '';
40968 this.inputEl().dom.value = '';
40975 getRawValue : function()
40977 var v = this.inputEl().getValue();
40982 getValue : function()
40984 return this.fixPrecision(this.parseValue(this.getRawValue()));
40987 parseValue : function(value)
40989 if(this.thousandsDelimiter) {
40991 r = new RegExp(",", "g");
40992 value = value.replace(r, "");
40995 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40996 return isNaN(value) ? '' : value;
41000 fixPrecision : function(value)
41002 if(this.thousandsDelimiter) {
41004 r = new RegExp(",", "g");
41005 value = value.replace(r, "");
41008 var nan = isNaN(value);
41010 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41011 return nan ? '' : value;
41013 return parseFloat(value).toFixed(this.decimalPrecision);
41016 decimalPrecisionFcn : function(v)
41018 return Math.floor(v);
41021 validateValue : function(value)
41023 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41027 var num = this.parseValue(value);
41030 this.markInvalid(String.format(this.nanText, value));
41034 if(num < this.minValue){
41035 this.markInvalid(String.format(this.minText, this.minValue));
41039 if(num > this.maxValue){
41040 this.markInvalid(String.format(this.maxText, this.maxValue));
41047 validate : function()
41049 if(this.disabled || this.allowBlank){
41054 var currency = this.getCurrency();
41056 if(this.validateValue(this.getRawValue()) && currency.length){
41061 this.markInvalid();
41065 getName: function()
41070 beforeBlur : function()
41076 var v = this.parseValue(this.getRawValue());
41083 onBlur : function()
41087 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41088 //this.el.removeClass(this.focusClass);
41091 this.hasFocus = false;
41093 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41097 var v = this.getValue();
41099 if(String(v) !== String(this.startValue)){
41100 this.fireEvent('change', this, v, this.startValue);
41103 this.fireEvent("blur", this);
41106 inputEl : function()
41108 return this.el.select('.roo-money-amount-input', true).first();
41111 currencyEl : function()
41113 return this.el.select('.roo-money-currency-input', true).first();
41116 hiddenEl : function()
41118 return this.el.select('input.hidden-number-input',true).first();